diff --git a/packages/bruno-js/src/bruno-request.js b/packages/bruno-js/src/bruno-request.js index 24af77598..372a45f76 100644 --- a/packages/bruno-js/src/bruno-request.js +++ b/packages/bruno-js/src/bruno-request.js @@ -1,11 +1,34 @@ class BrunoRequest { + /** + * The following properties are available as shorthand: + * - req.url + * - req.method + * - req.headers + * - req.timeout + * - req.body + * + * Above shorthands are useful for accessing the request properties directly in the scripts + * It must be noted that the user cannot set these properties directly. + * They should use the respective setter methods to set these properties. + */ constructor(req) { this.req = req; this.url = req.url; this.method = req.method; this.headers = req.headers; - this.body = req.data; this.timeout = req.timeout; + + /** + * We automatically parse the JSON body if the content type is JSON + * This is to make it easier for the user to access the body directly + * + * It must be noted that the request data is always a string and is what gets sent over the network + * If the user wants to access the raw data, they can use getBody({raw: true}) method + */ + const isJson = hasJSONContentType(this.req.headers); + if (isJson) { + this.body = this.__safeParseJSON(req.data); + } } getUrl() { @@ -13,6 +36,7 @@ class BrunoRequest { } setUrl(url) { + this.url = url; this.req.url = url; } @@ -37,6 +61,7 @@ class BrunoRequest { } setMethod(method) { + this.method = method; this.req.method = method; } @@ -45,6 +70,7 @@ class BrunoRequest { } setHeaders(headers) { + this.headers = headers; this.req.headers = headers; } @@ -53,35 +79,60 @@ class BrunoRequest { } setHeader(name, value) { + this.headers[name] = value; this.req.headers[name] = value; } + hasJSONContentType(headers) { + const contentType = headers?.['Content-Type'] || headers?.['content-type'] || ''; + return contentType.includes('json'); + } + /** * Get the body of the request * - * @param {*} options - * @param {boolean} options.raw - If true, return the raw body without parsing it - * @returns + * We automatically parse and return the JSON body if the content type is JSON + * If the user wants the raw body, they can pass the raw option as true */ getBody(options = {}) { - let headers = this.req.headers; - const contentType = headers?.['Content-Type'] || headers?.['content-type'] || ''; - const hasJSONContentType = contentType.includes('json'); + if (options.raw) { + return this.req.data; + } - if (hasJSONContentType && !options.raw) { - return JSON.parse(this.req.data); + const isJson = hasJSONContentType(this.req.headers); + if (isJson) { + return this.__safeParseJSON(this.req.data); } return this.req.data; } - setBody(data) { - if (typeof data === 'object') { - this.req.data = JSON.stringify(data); + /** + * If the content type is JSON and if the data is an object + * - We set the body property as the object itself + * - We set the request data as the stringified JSON as it is what gets sent over the network + * Otherwise + * - We set the request data as the data itself + * - We set the body property as the data itself + * + * If the user wants to override this behavior, they can pass the raw option as true + */ + setBody(data, options = {}) { + if (options.raw) { + this.req.data = data; + this.body = data; + return; + } + + const isJson = hasJSONContentType(this.req.headers); + if (isJson && this.__isObject(data)) { + this.body = data; + this.req.data = this.__safeStringifyJSON(data); return; } this.req.data = data; + this.body = data; } setMaxRedirects(maxRedirects) { @@ -93,8 +144,29 @@ class BrunoRequest { } setTimeout(timeout) { + this.timeout = timeout; this.req.timeout = timeout; } + + __safeParseJSON(str) { + try { + return JSON.parse(str); + } catch (e) { + return str; + } + } + + __safeStringifyJSON(obj) { + try { + return JSON.stringify(obj); + } catch (e) { + return obj; + } + } + + __isObject(obj) { + return obj !== null && typeof obj === 'object'; + } } module.exports = BrunoRequest;