mirror of
https://github.com/usebruno/bruno.git
synced 2024-11-21 23:43:15 +01:00
Improve how the URL values are transformed in postman export. (#3025)
* Improve how the URL values are transformed. * Made few changes and also added jsdoc comments * 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. * Moved the transformUrl logic to a different file for testing. Added new tests. * Added tests and updated sanitizeUrl function. * Updates made in jsdocs. * Updated function params. * Review: Code restructure. * Small changes made. * Updated the return value when there is an error. * Changes
This commit is contained in:
parent
07baa63e9d
commit
00fcd30348
@ -1,6 +1,105 @@
|
||||
import map from 'lodash/map';
|
||||
import * as FileSaver from 'file-saver';
|
||||
import { deleteSecretsInEnvs, deleteUidsInEnvs, deleteUidsInItems } from 'utils/collections/export';
|
||||
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|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()) {
|
||||
throw new Error("Invalid URL input");
|
||||
}
|
||||
|
||||
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 ? host.split(urlRegexPatterns.domainSegmentSeparator) : [];
|
||||
postmanUrl.path = path ? path.split(urlRegexPatterns.pathSegmentSeparator) : [];
|
||||
} 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(/(?<!:)\/{2,}/g, '/');
|
||||
};
|
||||
|
||||
/**
|
||||
* Replaces all `\\` (backslashes) with `//` (forward slashes) and collapses multiple slashes into one.
|
||||
*
|
||||
* @param {string} url - The URL to sanitize.
|
||||
* @returns {string} The sanitized URL.
|
||||
*
|
||||
*/
|
||||
export const sanitizeUrl = (url) => {
|
||||
let sanitizedUrl = collapseDuplicateSlashes(url.replace(/\\/g, '//'));
|
||||
return sanitizedUrl;
|
||||
};
|
||||
|
||||
export const exportCollection = (collection) => {
|
||||
delete collection.uid;
|
||||
@ -177,49 +276,17 @@ 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')
|
||||
.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 }));
|
||||
};
|
||||
|
||||
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)
|
||||
// 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)
|
||||
};
|
||||
|
||||
if (itemRequest.body.mode != 'none') {
|
||||
if (itemRequest.body.mode !== 'none') {
|
||||
requestObject.body = generateBody(itemRequest.body);
|
||||
}
|
||||
return requestObject;
|
||||
|
@ -0,0 +1,81 @@
|
||||
const { sanitizeUrl, transformUrl } = require('./postman-collection');
|
||||
|
||||
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: []
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
})
|
Loading…
Reference in New Issue
Block a user