From 584534682ec332c91a3168b2f7b7db73a46fb3a6 Mon Sep 17 00:00:00 2001 From: Sanjai Kumar Date: Wed, 4 Sep 2024 18:14:54 +0530 Subject: [PATCH 01/11] Improve how the URL values are transformed. --- .../src/utils/exporters/postman-collection.js | 47 ++++++++++--------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/packages/bruno-app/src/utils/exporters/postman-collection.js b/packages/bruno-app/src/utils/exporters/postman-collection.js index 7260942f4d..3c662cb0f6 100644 --- a/packages/bruno-app/src/utils/exporters/postman-collection.js +++ b/packages/bruno-app/src/utils/exporters/postman-collection.js @@ -177,20 +177,6 @@ export const exportCollection = (collection) => { } }; - const generateHost = (url) => { - try { - const { hostname } = new URL(url); - return hostname.split('.'); - } catch (error) { - console.error(`Invalid URL: ${url}`, error); - return []; - } - }; - - const generatePathParams = (params) => { - return params.filter((param) => param.type === 'path').map((param) => `:${param.name}`); - }; - const generateQueryParams = (params) => { return params .filter((param) => param.type === 'query') @@ -203,20 +189,37 @@ export const exportCollection = (collection) => { .map(({ name, value, description }) => ({ key: name, value, description })); }; + const transformUrl = (url, params) => { + const postmanUrl = { raw: url }; + const urlParts = url.split(/:\/\//); + let rawHostAndPath; + + if (urlParts.length === 1) { + rawHostAndPath = urlParts[0]; + } else if (urlParts.length === 2) { + postmanUrl.protocol = urlParts[0]; + rawHostAndPath = urlParts[1]; + } else { + console.error(`Invalid URL: ${url}`); + return {}; + } + + const hostAndPath = rawHostAndPath.split(/\/(.+)/); + postmanUrl.host = hostAndPath[0].split(/\./); + postmanUrl.path = hostAndPath[1] === undefined ? [] : hostAndPath[1].split(/\//); + postmanUrl.query = generateQueryParams(params); + postmanUrl.variable = generateVariables(params); + + return postmanUrl; + }; + const generateRequestSection = (itemRequest) => { const requestObject = { method: itemRequest.method, header: generateHeaders(itemRequest.headers), auth: generateAuth(itemRequest.auth), description: itemRequest.docs, - url: { - raw: itemRequest.url, - host: generateHost(itemRequest.url), - path: generatePathParams(itemRequest.params), - query: generateQueryParams(itemRequest.params), - variable: generateVariables(itemRequest.params) - }, - auth: generateAuth(itemRequest.auth) + url: transformUrl(itemRequest.url, itemRequest.params) }; if (itemRequest.body.mode != 'none') { From 4ee9cf4787a1d47dcfc13a887a062a4608fc3fec Mon Sep 17 00:00:00 2001 From: Sanjai Kumar Date: Thu, 5 Sep 2024 15:20:37 +0530 Subject: [PATCH 02/11] Made few changes and also added jsdoc comments --- .../src/utils/exporters/postman-collection.js | 65 +++++++++++++++---- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/packages/bruno-app/src/utils/exporters/postman-collection.js b/packages/bruno-app/src/utils/exporters/postman-collection.js index 3c662cb0f6..9f2d886897 100644 --- a/packages/bruno-app/src/utils/exporters/postman-collection.js +++ b/packages/bruno-app/src/utils/exporters/postman-collection.js @@ -189,24 +189,63 @@ export const exportCollection = (collection) => { .map(({ name, value, description }) => ({ key: name, value, description })); }; +/** + * Transforms a given URL string into an object representing the protocol, host, path, query, and variables. + * + * @param {string} url - The raw URL to be transformed. + * @param {Object} params - The params object. + * @returns {Object} An object containing the URL's protocol, host, path, query, and variables. + */ const transformUrl = (url, params) => { + const urlRegexPatterns = { + protocolSeparator: /:\/\//, + hostPathSeparator: /\/(.+)/, + domainSegmentSeparator: /\./, + pathSegmentSeparator: /\// + }; + const postmanUrl = { raw: url }; - const urlParts = url.split(/:\/\//); - let rawHostAndPath; - if (urlParts.length === 1) { - rawHostAndPath = urlParts[0]; - } else if (urlParts.length === 2) { - postmanUrl.protocol = urlParts[0]; - rawHostAndPath = urlParts[1]; - } else { - console.error(`Invalid URL: ${url}`); + /** + * Splits a URL into its protocol, host and path. + * + * @param {string} url - The URL to be split. + * @returns {Object} An object containing the protocol and the raw host/path string. + */ + const splitUrl = (url) => { + const urlParts = url.split(urlRegexPatterns.protocolSeparator); + if (urlParts.length === 1) { + return { protocol: '', rawHostAndPath: urlParts[0] }; + } else if (urlParts.length === 2) { + return { protocol: urlParts[0], rawHostAndPath: urlParts[1] }; + } else { + throw new Error(`Invalid URL format: ${url}`); + } + }; + + /** + * Splits the host and path from a raw host/path string. + * + * @param {string} rawHostAndPath - The raw host and path string to be split. + * @returns {Object} An object containing the host and path. + */ + const splitHostAndPath = (rawHostAndPath) => { + const [host, path = ''] = rawHostAndPath.split(urlRegexPatterns.hostPathSeparator); + return { host, path }; + }; + + try { + const { protocol, rawHostAndPath } = splitUrl(url); + postmanUrl.protocol = protocol; + + const { host, path } = splitHostAndPath(rawHostAndPath); + postmanUrl.host = host.split(urlRegexPatterns.domainSegmentSeparator); + postmanUrl.path = path ? path.split(urlRegexPatterns.pathSegmentSeparator).filter(Boolean) : []; + } catch (error) { + console.error(error.message); return {}; } - const hostAndPath = rawHostAndPath.split(/\/(.+)/); - postmanUrl.host = hostAndPath[0].split(/\./); - postmanUrl.path = hostAndPath[1] === undefined ? [] : hostAndPath[1].split(/\//); postmanUrl.query = generateQueryParams(params); postmanUrl.variable = generateVariables(params); @@ -222,7 +261,7 @@ export const exportCollection = (collection) => { url: transformUrl(itemRequest.url, itemRequest.params) }; - if (itemRequest.body.mode != 'none') { + if (itemRequest.body.mode !== 'none') { requestObject.body = generateBody(itemRequest.body); } return requestObject; From 186d21c694349e32c554bad6e55a2c473ab786b9 Mon Sep 17 00:00:00 2001 From: Sanjai Kumar Date: Thu, 5 Sep 2024 15:58:03 +0530 Subject: [PATCH 03/11] Removed the querystring values that are getting appended in the host array by filtering you the the queryvalues as we already have the queryparams values inside the request.params object. --- .../src/utils/exporters/postman-collection.js | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/packages/bruno-app/src/utils/exporters/postman-collection.js b/packages/bruno-app/src/utils/exporters/postman-collection.js index 9f2d886897..030671f0b8 100644 --- a/packages/bruno-app/src/utils/exporters/postman-collection.js +++ b/packages/bruno-app/src/utils/exporters/postman-collection.js @@ -189,19 +189,20 @@ export const exportCollection = (collection) => { .map(({ name, value, description }) => ({ key: name, value, description })); }; -/** - * Transforms a given URL string into an object representing the protocol, host, path, query, and variables. - * - * @param {string} url - The raw URL to be transformed. - * @param {Object} params - The params object. - * @returns {Object} An object containing the URL's protocol, host, path, query, and variables. - */ + /** + * Transforms a given URL string into an object representing the protocol, host, path, query, and variables. + * + * @param {string} url - The raw URL to be transformed. + * @param {Object} params - The params object. + * @returns {Object} An object containing the URL's protocol, host, path, query, and variables. + */ const transformUrl = (url, params) => { const urlRegexPatterns = { - protocolSeparator: /:\/\//, - hostPathSeparator: /\/(.+)/, + protocolAndRestSeparator: /:\/\//, + hostAndPathSeparator: /\/(.+)/, domainSegmentSeparator: /\./, - pathSegmentSeparator: /\// + pathSegmentSeparator: /\//, + queryStringSeparator: /\?/ }; const postmanUrl = { raw: url }; @@ -213,11 +214,12 @@ export const exportCollection = (collection) => { * @returns {Object} An object containing the protocol and the raw host/path string. */ const splitUrl = (url) => { - const urlParts = url.split(urlRegexPatterns.protocolSeparator); + const urlParts = url.split(urlRegexPatterns.protocolAndRestSeparator); if (urlParts.length === 1) { return { protocol: '', rawHostAndPath: urlParts[0] }; } else if (urlParts.length === 2) { - return { protocol: urlParts[0], rawHostAndPath: urlParts[1] }; + const [hostAndPath, _] = urlParts[1].split(urlRegexPatterns.queryStringSeparator); + return { protocol: urlParts[0], rawHostAndPath: hostAndPath }; } else { throw new Error(`Invalid URL format: ${url}`); } @@ -230,7 +232,7 @@ export const exportCollection = (collection) => { * @returns {Object} An object containing the host and path. */ const splitHostAndPath = (rawHostAndPath) => { - const [host, path = ''] = rawHostAndPath.split(urlRegexPatterns.hostPathSeparator); + const [host, path = ''] = rawHostAndPath.split(urlRegexPatterns.hostAndPathSeparator); return { host, path }; }; From fa6cfabffc3511785d1f61ecd86e801542767b69 Mon Sep 17 00:00:00 2001 From: Sanjai Kumar Date: Wed, 11 Sep 2024 11:51:11 +0530 Subject: [PATCH 04/11] Moved the transformUrl logic to a different file for testing. Added new tests. --- .../bruno-app/src/utils/collections/export.js | 72 +++++++++++++++++ .../src/utils/exporters/postman-collection.js | 79 +------------------ .../exporters/postman-collection.spec.js | 62 +++++++++++++++ 3 files changed, 135 insertions(+), 78 deletions(-) create mode 100644 packages/bruno-app/src/utils/exporters/postman-collection.spec.js diff --git a/packages/bruno-app/src/utils/collections/export.js b/packages/bruno-app/src/utils/collections/export.js index 5ef7b1b49a..e157d571fe 100644 --- a/packages/bruno-app/src/utils/collections/export.js +++ b/packages/bruno-app/src/utils/collections/export.js @@ -79,4 +79,76 @@ export const exportCollection = (collection) => { FileSaver.saveAs(fileBlob, fileName); }; +/** + * Transforms a given URL string into an object representing the protocol, host, path, query, and variables. + * + * @param {string} url - The raw URL to be transformed. + * @param {Object} params - The params object. + * @returns {Object} An object containing the URL's protocol, host, path, query, and variables. + */ +export const transformUrl = (url, params) => { + const urlRegexPatterns = { + protocolAndRestSeparator: /:\/\//, + hostAndPathSeparator: /\/(.+)/, + domainSegmentSeparator: /\./, + pathSegmentSeparator: /\//, + queryStringSeparator: /\?/ + }; + + const postmanUrl = { raw: url }; + + /** + * Splits a URL into its protocol, host and path. + * + * @param {string} url - The URL to be split. + * @returns {Object} An object containing the protocol and the raw host/path string. + */ + const splitUrl = (url) => { + const urlParts = url.split(urlRegexPatterns.protocolAndRestSeparator); + if (urlParts.length === 1) { + return { protocol: '', rawHostAndPath: urlParts[0] }; + } else if (urlParts.length === 2) { + const [hostAndPath, _] = urlParts[1].split(urlRegexPatterns.queryStringSeparator); + return { protocol: urlParts[0], rawHostAndPath: hostAndPath }; + } else { + throw new Error(`Invalid URL format: ${url}`); + } + }; + + /** + * Splits the host and path from a raw host/path string. + * + * @param {string} rawHostAndPath - The raw host and path string to be split. + * @returns {Object} An object containing the host and path. + */ + const splitHostAndPath = (rawHostAndPath) => { + const [host, path = ''] = rawHostAndPath.split(urlRegexPatterns.hostAndPathSeparator); + return { host, path }; + }; + + try { + const { protocol, rawHostAndPath } = splitUrl(url); + postmanUrl.protocol = protocol; + + const { host, path } = splitHostAndPath(rawHostAndPath); + postmanUrl.host = host.split(urlRegexPatterns.domainSegmentSeparator); + postmanUrl.path = path ? path.split(urlRegexPatterns.pathSegmentSeparator).filter(Boolean) : []; + } catch (error) { + console.error(error.message); + return {}; + } + + // Construct query params. + postmanUrl.query = params + .filter((param) => param.type === 'query') + .map(({ name, value, description }) => ({ key: name, value, description })); + + // Construct path params. + postmanUrl.variable = params + .filter((param) => param.type === 'path') + .map(({ name, value, description }) => ({ key: name, value, description })); + + return postmanUrl; +}; + export default exportCollection; diff --git a/packages/bruno-app/src/utils/exporters/postman-collection.js b/packages/bruno-app/src/utils/exporters/postman-collection.js index 030671f0b8..3e6c9784e2 100644 --- a/packages/bruno-app/src/utils/exporters/postman-collection.js +++ b/packages/bruno-app/src/utils/exporters/postman-collection.js @@ -1,6 +1,6 @@ import map from 'lodash/map'; import * as FileSaver from 'file-saver'; -import { deleteSecretsInEnvs, deleteUidsInEnvs, deleteUidsInItems } from 'utils/collections/export'; +import { deleteSecretsInEnvs, deleteUidsInEnvs, deleteUidsInItems, transformUrl } from 'utils/collections/export'; export const exportCollection = (collection) => { delete collection.uid; @@ -177,83 +177,6 @@ export const exportCollection = (collection) => { } }; - const generateQueryParams = (params) => { - return params - .filter((param) => param.type === 'query') - .map(({ name, value, description }) => ({ key: name, value, description })); - }; - - const generateVariables = (params) => { - return params - .filter((param) => param.type === 'path') - .map(({ name, value, description }) => ({ key: name, value, description })); - }; - - /** - * Transforms a given URL string into an object representing the protocol, host, path, query, and variables. - * - * @param {string} url - The raw URL to be transformed. - * @param {Object} params - The params object. - * @returns {Object} An object containing the URL's protocol, host, path, query, and variables. - */ - const transformUrl = (url, params) => { - const urlRegexPatterns = { - protocolAndRestSeparator: /:\/\//, - hostAndPathSeparator: /\/(.+)/, - domainSegmentSeparator: /\./, - pathSegmentSeparator: /\//, - queryStringSeparator: /\?/ - }; - - const postmanUrl = { raw: url }; - - /** - * Splits a URL into its protocol, host and path. - * - * @param {string} url - The URL to be split. - * @returns {Object} An object containing the protocol and the raw host/path string. - */ - const splitUrl = (url) => { - const urlParts = url.split(urlRegexPatterns.protocolAndRestSeparator); - if (urlParts.length === 1) { - return { protocol: '', rawHostAndPath: urlParts[0] }; - } else if (urlParts.length === 2) { - const [hostAndPath, _] = urlParts[1].split(urlRegexPatterns.queryStringSeparator); - return { protocol: urlParts[0], rawHostAndPath: hostAndPath }; - } else { - throw new Error(`Invalid URL format: ${url}`); - } - }; - - /** - * Splits the host and path from a raw host/path string. - * - * @param {string} rawHostAndPath - The raw host and path string to be split. - * @returns {Object} An object containing the host and path. - */ - const splitHostAndPath = (rawHostAndPath) => { - const [host, path = ''] = rawHostAndPath.split(urlRegexPatterns.hostAndPathSeparator); - return { host, path }; - }; - - try { - const { protocol, rawHostAndPath } = splitUrl(url); - postmanUrl.protocol = protocol; - - const { host, path } = splitHostAndPath(rawHostAndPath); - postmanUrl.host = host.split(urlRegexPatterns.domainSegmentSeparator); - postmanUrl.path = path ? path.split(urlRegexPatterns.pathSegmentSeparator).filter(Boolean) : []; - } catch (error) { - console.error(error.message); - return {}; - } - - postmanUrl.query = generateQueryParams(params); - postmanUrl.variable = generateVariables(params); - - return postmanUrl; - }; - const generateRequestSection = (itemRequest) => { const requestObject = { method: itemRequest.method, diff --git a/packages/bruno-app/src/utils/exporters/postman-collection.spec.js b/packages/bruno-app/src/utils/exporters/postman-collection.spec.js new file mode 100644 index 0000000000..62e479d755 --- /dev/null +++ b/packages/bruno-app/src/utils/exporters/postman-collection.spec.js @@ -0,0 +1,62 @@ +const { transformUrl } = require('../collections/export'); + +describe('transformUrl', () => { + it('should handle basic URL with path variables', () => { + const url = 'https://example.com/{{username}}/api/resource/:id'; + const params = [ + { name: 'id', value: '123', type: 'path' }, + ]; + + const result = transformUrl(url, params); + + expect(result).toEqual({ + raw: 'https://example.com/{{username}}/api/resource/:id', + protocol: 'https', + host: ['example', 'com'], + path: ['{{username}}', 'api', 'resource', ':id'], + query: [], + variable: [ + { key: 'id', value: '123' }, + ] + }); + }); + + it('should handle URL with query parameters', () => { + const url = 'https://example.com/api/resource?limit=10&offset=20'; + const params = [ + { name: 'limit', value: '10', type: 'query' }, + { name: 'offset', value: '20', type: 'query' } + ]; + + const result = transformUrl(url, params); + + expect(result).toEqual({ + raw: 'https://example.com/api/resource?limit=10&offset=20', + protocol: 'https', + host: ['example', 'com'], + path: ['api', 'resource'], + query: [ + { key: 'limit', value: '10' }, + { key: 'offset', value: '20' } + ], + variable: [] + }); + }); + + it('should handle URL without protocol', () => { + const url = 'example.com/api/resource'; + const params = []; + + const result = transformUrl(url, params); + + expect(result).toEqual({ + raw: 'example.com/api/resource', + protocol: '', + host: ['example', 'com'], + path: ['api', 'resource'], + query: [], + variable: [] + }); + }); +}); + From 088af8efc4239da90d0551941ae3f74227f939ef Mon Sep 17 00:00:00 2001 From: Sanjai Kumar Date: Wed, 11 Sep 2024 15:12:35 +0530 Subject: [PATCH 05/11] Added tests and updated sanitizeUrl function. --- .../src/utils/exporters/postman-collection.js | 4 ++- packages/bruno-app/src/utils/url/index.js | 27 +++++++++++++++++++ .../bruno-app/src/utils/url/index.spec.js | 22 ++++++++++++++- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/packages/bruno-app/src/utils/exporters/postman-collection.js b/packages/bruno-app/src/utils/exporters/postman-collection.js index 3e6c9784e2..793163010f 100644 --- a/packages/bruno-app/src/utils/exporters/postman-collection.js +++ b/packages/bruno-app/src/utils/exporters/postman-collection.js @@ -1,6 +1,7 @@ import map from 'lodash/map'; import * as FileSaver from 'file-saver'; import { deleteSecretsInEnvs, deleteUidsInEnvs, deleteUidsInItems, transformUrl } from 'utils/collections/export'; +import { sanitizeUrl } from 'utils/url/index'; export const exportCollection = (collection) => { delete collection.uid; @@ -183,7 +184,8 @@ export const exportCollection = (collection) => { header: generateHeaders(itemRequest.headers), auth: generateAuth(itemRequest.auth), description: itemRequest.docs, - url: transformUrl(itemRequest.url, itemRequest.params) + // We clean up the URL to make sure it's in the right format. This means changing backslashes to forward slashes and reducing multiple slashes to a single one, except in the protocol part. + url: transformUrl(sanitizeUrl(itemRequest.url), itemRequest.params) }; if (itemRequest.body.mode !== 'none') { diff --git a/packages/bruno-app/src/utils/url/index.js b/packages/bruno-app/src/utils/url/index.js index f9557b3c46..daf5104975 100644 --- a/packages/bruno-app/src/utils/url/index.js +++ b/packages/bruno-app/src/utils/url/index.js @@ -17,6 +17,33 @@ const hasLength = (str) => { return str.length > 0; }; +/** + * The regex pattern matches sequences of two or more slashes and replaces them with a single slash. + * + * @link https://github.com/withastro/astro/blob/1c64ae304d3c008a776e98e48fd3ece8be7b1fb5/packages/internal-helpers/src/path.ts#L18-L20 + * @param {String} path A URL string + * @returns {String} The sanitized URL + * + */ +const collapseDuplicateSlashes = (path) => { + return path.replace(/(? { + let sanitizedUrl = collapseDuplicateSlashes(url.replace(/\\/g, '//')); + return sanitizedUrl; +}; + export const parseQueryParams = (query) => { try { if (!query || !query.length) { diff --git a/packages/bruno-app/src/utils/url/index.spec.js b/packages/bruno-app/src/utils/url/index.spec.js index 0645befeef..d52b8e51b7 100644 --- a/packages/bruno-app/src/utils/url/index.spec.js +++ b/packages/bruno-app/src/utils/url/index.spec.js @@ -1,4 +1,4 @@ -import { parseQueryParams, splitOnFirst, parsePathParams, interpolateUrl, interpolateUrlPathParams } from './index'; +import { parseQueryParams, splitOnFirst, parsePathParams, interpolateUrl, interpolateUrlPathParams, sanitizeUrl } from './index'; describe('Url Utils - parseQueryParams', () => { it('should parse query - case 1', () => { @@ -209,3 +209,23 @@ describe('Url Utils - interpolateUrl, interpolateUrlPathParams', () => { expect(result).toEqual(expectedUrl); }); }); + +describe('Url Utils - sanitizeUrl', () => { + test('should replace backslashes with slashes', () => { + const input = 'http:\\\\example.com\\path\\to\\file'; + const expected = 'http://example.com/path/to/file'; + expect(sanitizeUrl(input)).toBe(expected); + }); + + test('should collapse multiple slashes into a single slash', () => { + const input = 'http://example.com//path///to////file'; + const expected = 'http://example.com/path/to/file'; + expect(sanitizeUrl(input)).toBe(expected); + }); + + test('should handle URLs with mixed slashes', () => { + const input = 'http:\\example.com//path\\to//file'; + const expected = 'http://example.com/path/to/file'; + expect(sanitizeUrl(input)).toBe(expected); + }); +}); From 89716a193b7a3fec3eee8dff46ebe6899c233b79 Mon Sep 17 00:00:00 2001 From: Sanjai Kumar Date: Wed, 11 Sep 2024 15:38:05 +0530 Subject: [PATCH 06/11] Updates made in jsdocs. --- .../src/utils/exporters/postman-collection.js | 2 +- packages/bruno-app/src/utils/url/index.js | 12 ++++-------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/bruno-app/src/utils/exporters/postman-collection.js b/packages/bruno-app/src/utils/exporters/postman-collection.js index 793163010f..25a434ba60 100644 --- a/packages/bruno-app/src/utils/exporters/postman-collection.js +++ b/packages/bruno-app/src/utils/exporters/postman-collection.js @@ -184,7 +184,7 @@ export const exportCollection = (collection) => { header: generateHeaders(itemRequest.headers), auth: generateAuth(itemRequest.auth), description: itemRequest.docs, - // We clean up the URL to make sure it's in the right format. This means changing backslashes to forward slashes and reducing multiple slashes to a single one, except in the protocol part. + // We sanitize the URL to make sure it's in the right format before passing it to the transformUrl func. This means changing backslashes to forward slashes and reducing multiple slashes to a single one, except in the protocol part. url: transformUrl(sanitizeUrl(itemRequest.url), itemRequest.params) }; diff --git a/packages/bruno-app/src/utils/url/index.js b/packages/bruno-app/src/utils/url/index.js index daf5104975..031ccf164d 100644 --- a/packages/bruno-app/src/utils/url/index.js +++ b/packages/bruno-app/src/utils/url/index.js @@ -18,23 +18,19 @@ const hasLength = (str) => { }; /** - * The regex pattern matches sequences of two or more slashes and replaces them with a single slash. + * Collapses multiple consecutive slashes (`//`) into a single slash, while skipping the protocol (e.g., `http://` or `https://`). * - * @link https://github.com/withastro/astro/blob/1c64ae304d3c008a776e98e48fd3ece8be7b1fb5/packages/internal-helpers/src/path.ts#L18-L20 - * @param {String} path A URL string + * @param {String} url A URL string * @returns {String} The sanitized URL * */ -const collapseDuplicateSlashes = (path) => { +const collapseDuplicateSlashes = (url) => { return path.replace(/(? Date: Wed, 11 Sep 2024 15:41:50 +0530 Subject: [PATCH 07/11] Updated function params. --- packages/bruno-app/src/utils/url/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/bruno-app/src/utils/url/index.js b/packages/bruno-app/src/utils/url/index.js index 031ccf164d..0d2f51e5e9 100644 --- a/packages/bruno-app/src/utils/url/index.js +++ b/packages/bruno-app/src/utils/url/index.js @@ -20,12 +20,12 @@ const hasLength = (str) => { /** * Collapses multiple consecutive slashes (`//`) into a single slash, while skipping the protocol (e.g., `http://` or `https://`). * - * @param {String} url A URL string + * @param {String} url - A URL string * @returns {String} The sanitized URL * */ const collapseDuplicateSlashes = (url) => { - return path.replace(/(? Date: Wed, 18 Sep 2024 16:42:11 +0530 Subject: [PATCH 08/11] Review: Code restructure. --- .../bruno-app/src/utils/collections/export.js | 72 -------------- .../src/utils/exporters/postman-collection.js | 98 ++++++++++++++++++- .../exporters/postman-collection.spec.js | 21 +++- packages/bruno-app/src/utils/url/index.js | 23 ----- .../bruno-app/src/utils/url/index.spec.js | 24 +---- 5 files changed, 118 insertions(+), 120 deletions(-) diff --git a/packages/bruno-app/src/utils/collections/export.js b/packages/bruno-app/src/utils/collections/export.js index e157d571fe..5ef7b1b49a 100644 --- a/packages/bruno-app/src/utils/collections/export.js +++ b/packages/bruno-app/src/utils/collections/export.js @@ -79,76 +79,4 @@ export const exportCollection = (collection) => { FileSaver.saveAs(fileBlob, fileName); }; -/** - * Transforms a given URL string into an object representing the protocol, host, path, query, and variables. - * - * @param {string} url - The raw URL to be transformed. - * @param {Object} params - The params object. - * @returns {Object} An object containing the URL's protocol, host, path, query, and variables. - */ -export const transformUrl = (url, params) => { - const urlRegexPatterns = { - protocolAndRestSeparator: /:\/\//, - hostAndPathSeparator: /\/(.+)/, - domainSegmentSeparator: /\./, - pathSegmentSeparator: /\//, - queryStringSeparator: /\?/ - }; - - const postmanUrl = { raw: url }; - - /** - * Splits a URL into its protocol, host and path. - * - * @param {string} url - The URL to be split. - * @returns {Object} An object containing the protocol and the raw host/path string. - */ - const splitUrl = (url) => { - const urlParts = url.split(urlRegexPatterns.protocolAndRestSeparator); - if (urlParts.length === 1) { - return { protocol: '', rawHostAndPath: urlParts[0] }; - } else if (urlParts.length === 2) { - const [hostAndPath, _] = urlParts[1].split(urlRegexPatterns.queryStringSeparator); - return { protocol: urlParts[0], rawHostAndPath: hostAndPath }; - } else { - throw new Error(`Invalid URL format: ${url}`); - } - }; - - /** - * Splits the host and path from a raw host/path string. - * - * @param {string} rawHostAndPath - The raw host and path string to be split. - * @returns {Object} An object containing the host and path. - */ - const splitHostAndPath = (rawHostAndPath) => { - const [host, path = ''] = rawHostAndPath.split(urlRegexPatterns.hostAndPathSeparator); - return { host, path }; - }; - - try { - const { protocol, rawHostAndPath } = splitUrl(url); - postmanUrl.protocol = protocol; - - const { host, path } = splitHostAndPath(rawHostAndPath); - postmanUrl.host = host.split(urlRegexPatterns.domainSegmentSeparator); - postmanUrl.path = path ? path.split(urlRegexPatterns.pathSegmentSeparator).filter(Boolean) : []; - } catch (error) { - console.error(error.message); - return {}; - } - - // Construct query params. - postmanUrl.query = params - .filter((param) => param.type === 'query') - .map(({ name, value, description }) => ({ key: name, value, description })); - - // Construct path params. - postmanUrl.variable = params - .filter((param) => param.type === 'path') - .map(({ name, value, description }) => ({ key: name, value, description })); - - return postmanUrl; -}; - export default exportCollection; diff --git a/packages/bruno-app/src/utils/exporters/postman-collection.js b/packages/bruno-app/src/utils/exporters/postman-collection.js index 25a434ba60..04c9ff9fbc 100644 --- a/packages/bruno-app/src/utils/exporters/postman-collection.js +++ b/packages/bruno-app/src/utils/exporters/postman-collection.js @@ -1,7 +1,101 @@ import map from 'lodash/map'; import * as FileSaver from 'file-saver'; -import { deleteSecretsInEnvs, deleteUidsInEnvs, deleteUidsInItems, transformUrl } from 'utils/collections/export'; -import { sanitizeUrl } from 'utils/url/index'; +import { deleteSecretsInEnvs, deleteUidsInEnvs, deleteUidsInItems } from '../collections/export'; + +/** + * Transforms a given URL string into an object representing the protocol, host, path, query, and variables. + * + * @param {string} url - The raw URL to be transformed. + * @param {Object} params - The params object. + * @returns {Object} An object containing the URL's protocol, host, path, query, and variables. + */ +export const transformUrl = (url, params) => { + const urlRegexPatterns = { + protocolAndRestSeparator: /:\/\//, + hostAndPathSeparator: /\/(.+)/, + domainSegmentSeparator: /\./, + pathSegmentSeparator: /\//, + queryStringSeparator: /\?/ + }; + + const postmanUrl = { raw: url }; + + /** + * Splits a URL into its protocol, host and path. + * + * @param {string} url - The URL to be split. + * @returns {Object} An object containing the protocol and the raw host/path string. + */ + const splitUrl = (url) => { + const urlParts = url.split(urlRegexPatterns.protocolAndRestSeparator); + if (urlParts.length === 1) { + return { protocol: '', rawHostAndPath: urlParts[0] }; + } else if (urlParts.length === 2) { + const [hostAndPath, _] = urlParts[1].split(urlRegexPatterns.queryStringSeparator); + return { protocol: urlParts[0], rawHostAndPath: hostAndPath }; + } else { + throw new Error(`Invalid URL format: ${url}`); + } + }; + + /** + * Splits the host and path from a raw host/path string. + * + * @param {string} rawHostAndPath - The raw host and path string to be split. + * @returns {Object} An object containing the host and path. + */ + const splitHostAndPath = (rawHostAndPath) => { + const [host, path = ''] = rawHostAndPath.split(urlRegexPatterns.hostAndPathSeparator); + return { host, path }; + }; + + try { + const { protocol, rawHostAndPath } = splitUrl(url); + postmanUrl.protocol = protocol; + + const { host, path } = splitHostAndPath(rawHostAndPath); + postmanUrl.host = host.split(urlRegexPatterns.domainSegmentSeparator); + postmanUrl.path = path ? path.split(urlRegexPatterns.pathSegmentSeparator).filter(Boolean) : []; + } catch (error) { + console.error(error.message); + return {}; + } + + // Construct query params. + postmanUrl.query = params + .filter((param) => param.type === 'query') + .map(({ name, value, description }) => ({ key: name, value, description })); + + // Construct path params. + postmanUrl.variable = params + .filter((param) => param.type === 'path') + .map(({ name, value, description }) => ({ key: name, value, description })); + + return postmanUrl; +}; + +/** + * Collapses multiple consecutive slashes (`//`) into a single slash, while skipping the protocol (e.g., `http://` or `https://`). + * + * @param {String} url - A URL string + * @returns {String} The sanitized URL + * + */ +const collapseDuplicateSlashes = (url) => { + return url.replace(/(? { + let sanitizedUrl = collapseDuplicateSlashes(url.replace(/\\/g, '//')); + return sanitizedUrl; +}; export const exportCollection = (collection) => { delete collection.uid; diff --git a/packages/bruno-app/src/utils/exporters/postman-collection.spec.js b/packages/bruno-app/src/utils/exporters/postman-collection.spec.js index 62e479d755..e2186aa97d 100644 --- a/packages/bruno-app/src/utils/exporters/postman-collection.spec.js +++ b/packages/bruno-app/src/utils/exporters/postman-collection.spec.js @@ -1,4 +1,4 @@ -const { transformUrl } = require('../collections/export'); +const { sanitizeUrl, transformUrl } = require('./postman-collection'); describe('transformUrl', () => { it('should handle basic URL with path variables', () => { @@ -60,3 +60,22 @@ describe('transformUrl', () => { }); }); +describe('sanitizeUrl', () => { + it('should replace backslashes with slashes', () => { + const input = 'http:\\\\example.com\\path\\to\\file'; + const expected = 'http://example.com/path/to/file'; + expect(sanitizeUrl(input)).toBe(expected); + }); + + it('should collapse multiple slashes into a single slash', () => { + const input = 'http://example.com//path///to////file'; + const expected = 'http://example.com/path/to/file'; + expect(sanitizeUrl(input)).toBe(expected); + }); + + it('should handle URLs with mixed slashes', () => { + const input = 'http:\\example.com//path\\to//file'; + const expected = 'http://example.com/path/to/file'; + expect(sanitizeUrl(input)).toBe(expected); + }); +}) \ No newline at end of file diff --git a/packages/bruno-app/src/utils/url/index.js b/packages/bruno-app/src/utils/url/index.js index 0d2f51e5e9..f9557b3c46 100644 --- a/packages/bruno-app/src/utils/url/index.js +++ b/packages/bruno-app/src/utils/url/index.js @@ -17,29 +17,6 @@ const hasLength = (str) => { return str.length > 0; }; -/** - * Collapses multiple consecutive slashes (`//`) into a single slash, while skipping the protocol (e.g., `http://` or `https://`). - * - * @param {String} url - A URL string - * @returns {String} The sanitized URL - * - */ -const collapseDuplicateSlashes = (url) => { - return url.replace(/(? { - let sanitizedUrl = collapseDuplicateSlashes(url.replace(/\\/g, '//')); - return sanitizedUrl; -}; - export const parseQueryParams = (query) => { try { if (!query || !query.length) { diff --git a/packages/bruno-app/src/utils/url/index.spec.js b/packages/bruno-app/src/utils/url/index.spec.js index d52b8e51b7..ec214d8141 100644 --- a/packages/bruno-app/src/utils/url/index.spec.js +++ b/packages/bruno-app/src/utils/url/index.spec.js @@ -1,4 +1,4 @@ -import { parseQueryParams, splitOnFirst, parsePathParams, interpolateUrl, interpolateUrlPathParams, sanitizeUrl } from './index'; +import { parseQueryParams, splitOnFirst, parsePathParams, interpolateUrl, interpolateUrlPathParams } from './index'; describe('Url Utils - parseQueryParams', () => { it('should parse query - case 1', () => { @@ -208,24 +208,4 @@ describe('Url Utils - interpolateUrl, interpolateUrlPathParams', () => { expect(result).toEqual(expectedUrl); }); -}); - -describe('Url Utils - sanitizeUrl', () => { - test('should replace backslashes with slashes', () => { - const input = 'http:\\\\example.com\\path\\to\\file'; - const expected = 'http://example.com/path/to/file'; - expect(sanitizeUrl(input)).toBe(expected); - }); - - test('should collapse multiple slashes into a single slash', () => { - const input = 'http://example.com//path///to////file'; - const expected = 'http://example.com/path/to/file'; - expect(sanitizeUrl(input)).toBe(expected); - }); - - test('should handle URLs with mixed slashes', () => { - const input = 'http:\\example.com//path\\to//file'; - const expected = 'http://example.com/path/to/file'; - expect(sanitizeUrl(input)).toBe(expected); - }); -}); +}); \ No newline at end of file From 78863e1c2ca5bec2531a623b2812a2c94daa3590 Mon Sep 17 00:00:00 2001 From: Sanjai Kumar Date: Wed, 18 Sep 2024 17:10:18 +0530 Subject: [PATCH 09/11] Small changes made. --- .../src/utils/exporters/postman-collection.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/bruno-app/src/utils/exporters/postman-collection.js b/packages/bruno-app/src/utils/exporters/postman-collection.js index 04c9ff9fbc..a03493c567 100644 --- a/packages/bruno-app/src/utils/exporters/postman-collection.js +++ b/packages/bruno-app/src/utils/exporters/postman-collection.js @@ -7,9 +7,13 @@ import { deleteSecretsInEnvs, deleteUidsInEnvs, deleteUidsInItems } from '../col * * @param {string} url - The raw URL to be transformed. * @param {Object} params - The params object. - * @returns {Object} An object containing the URL's protocol, host, path, query, and variables. + * @returns {Object|null} An object containing the URL's protocol, host, path, query, and variables, or null if an error occurs. */ export const transformUrl = (url, params) => { + if (typeof url !== 'string' || !url.trim()) { + throw new Error("Invalid URL input"); + } + const urlRegexPatterns = { protocolAndRestSeparator: /:\/\//, hostAndPathSeparator: /\/(.+)/, @@ -54,11 +58,11 @@ export const transformUrl = (url, params) => { postmanUrl.protocol = protocol; const { host, path } = splitHostAndPath(rawHostAndPath); - postmanUrl.host = host.split(urlRegexPatterns.domainSegmentSeparator); - postmanUrl.path = path ? path.split(urlRegexPatterns.pathSegmentSeparator).filter(Boolean) : []; + postmanUrl.host = host ? host.split(urlRegexPatterns.domainSegmentSeparator) : []; + postmanUrl.path = path ? path.split(urlRegexPatterns.pathSegmentSeparator) : []; } catch (error) { console.error(error.message); - return {}; + return null; } // Construct query params. From af693977ace22645438f36c36161659255853aed Mon Sep 17 00:00:00 2001 From: Sanjai Kumar Date: Wed, 18 Sep 2024 17:16:45 +0530 Subject: [PATCH 10/11] Updated the return value when there is an error. --- packages/bruno-app/src/utils/exporters/postman-collection.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/bruno-app/src/utils/exporters/postman-collection.js b/packages/bruno-app/src/utils/exporters/postman-collection.js index a03493c567..9dabc0f640 100644 --- a/packages/bruno-app/src/utils/exporters/postman-collection.js +++ b/packages/bruno-app/src/utils/exporters/postman-collection.js @@ -7,7 +7,7 @@ import { deleteSecretsInEnvs, deleteUidsInEnvs, deleteUidsInItems } from '../col * * @param {string} url - The raw URL to be transformed. * @param {Object} params - The params object. - * @returns {Object|null} An object containing the URL's protocol, host, path, query, and variables, or null if an error occurs. + * @returns {Object|null} An object containing the URL's protocol, host, path, query, and variables, or {} if an error occurs. */ export const transformUrl = (url, params) => { if (typeof url !== 'string' || !url.trim()) { @@ -62,7 +62,7 @@ export const transformUrl = (url, params) => { postmanUrl.path = path ? path.split(urlRegexPatterns.pathSegmentSeparator) : []; } catch (error) { console.error(error.message); - return null; + return {}; } // Construct query params. From c2ffefac45487bc9797cf5e06cef4d3c16077be9 Mon Sep 17 00:00:00 2001 From: Sanjai Kumar Date: Wed, 18 Sep 2024 17:48:09 +0530 Subject: [PATCH 11/11] Changes --- .../bruno-app/src/utils/exporters/postman-collection.spec.js | 2 +- packages/bruno-app/src/utils/url/index.spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/bruno-app/src/utils/exporters/postman-collection.spec.js b/packages/bruno-app/src/utils/exporters/postman-collection.spec.js index e2186aa97d..23c6690ccb 100644 --- a/packages/bruno-app/src/utils/exporters/postman-collection.spec.js +++ b/packages/bruno-app/src/utils/exporters/postman-collection.spec.js @@ -78,4 +78,4 @@ describe('sanitizeUrl', () => { const expected = 'http://example.com/path/to/file'; expect(sanitizeUrl(input)).toBe(expected); }); -}) \ No newline at end of file +}) diff --git a/packages/bruno-app/src/utils/url/index.spec.js b/packages/bruno-app/src/utils/url/index.spec.js index ec214d8141..0645befeef 100644 --- a/packages/bruno-app/src/utils/url/index.spec.js +++ b/packages/bruno-app/src/utils/url/index.spec.js @@ -208,4 +208,4 @@ describe('Url Utils - interpolateUrl, interpolateUrlPathParams', () => { expect(result).toEqual(expectedUrl); }); -}); \ No newline at end of file +});