diff --git a/package-lock.json b/package-lock.json index 86c63ffd5..5cb18d3bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26279,6 +26279,7 @@ "version": "2.0.0", "dependencies": { "@aws-sdk/credential-providers": "3.750.0", + "@faker-js/faker": "^9.5.1", "@usebruno/common": "0.1.0", "@usebruno/js": "0.12.0", "@usebruno/lang": "0.12.0", @@ -26786,6 +26787,23 @@ } } }, + "packages/bruno-electron/node_modules/@faker-js/faker": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.5.1.tgz", + "integrity": "sha512-0fzMEDxkExR2cn731kpDaCCnBGBUOIXEi2S1N5l8Hltp6aPf4soTMJ+g4k8r2sI5oB+rpwIW8Uy/6jkwGpnWPg==", + "deprecated": "Please update to a newer version", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/fakerjs" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.0.0", + "npm": ">=9.0.0" + } + }, "packages/bruno-electron/node_modules/@smithy/abort-controller": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.1.tgz", diff --git a/packages/bruno-electron/package.json b/packages/bruno-electron/package.json index a88dec6c8..11820c568 100644 --- a/packages/bruno-electron/package.json +++ b/packages/bruno-electron/package.json @@ -26,6 +26,7 @@ }, "dependencies": { "@aws-sdk/credential-providers": "3.750.0", + "@faker-js/faker": "^9.5.1", "@usebruno/common": "0.1.0", "@usebruno/js": "0.12.0", "@usebruno/lang": "0.12.0", @@ -60,7 +61,6 @@ "socks-proxy-agent": "^8.0.2", "tough-cookie": "^4.1.3", "uuid": "^9.0.0", - "@faker-js/faker": "^9.5.0", "yup": "^0.32.11" }, "optionalDependencies": { diff --git a/packages/bruno-electron/src/ipc/network/faker-functions.js b/packages/bruno-electron/src/ipc/network/faker-functions.js new file mode 100644 index 000000000..c97d262a2 --- /dev/null +++ b/packages/bruno-electron/src/ipc/network/faker-functions.js @@ -0,0 +1,127 @@ +const { faker } = require('@faker-js/faker'); + +const mockDataFunctions = { + guid: () => faker.string.uuid(), + timestamp: () => faker.date.anytime().getTime().toString(), + isoTimestamp: () => faker.date.anytime().toISOString(), + randomUUID: () => faker.string.uuid(), + randomAlphaNumeric: () => faker.string.alphanumeric(), + randomBoolean: () => faker.datatype.boolean(), + randomInt: () => faker.number.int(), + randomColor: () => faker.color.human(), + randomHexColor: () => faker.internet.color(), + randomAbbreviation: () => faker.hacker.abbreviation(), + randomIP: () => faker.internet.ip(), + randomIPV4: () => faker.internet.ipv4(), + randomIPV6: () => faker.internet.ipv6(), + randomMACAddress: () => faker.internet.mac(), + randomPassword: () => faker.internet.password(), + randomLocale: () => faker.location.countryCode(), + randomUserAgent: () => faker.internet.userAgent(), + randomProtocol: () => faker.internet.protocol(), + randomSemver: () => faker.system.semver(), + randomFirstName: () => faker.person.firstName(), + randomLastName: () => faker.person.lastName(), + randomFullName: () => faker.person.fullName(), + randomNamePrefix: () => faker.person.prefix(), + randomNameSuffix: () => faker.person.suffix(), + randomJobArea: () => faker.person.jobArea(), + randomJobDescriptor: () => faker.person.jobDescriptor(), + randomJobTitle: () => faker.person.jobTitle(), + randomJobType: () => faker.person.jobType(), + randomPhoneNumber: () => faker.phone.number(), + randomPhoneNumberExt: () => faker.phone.number(), + randomCity: () => faker.location.city(), + randomStreetName: () => faker.location.street(), + randomStreetAddress: () => faker.location.streetAddress(), + randomCountry: () => faker.location.country(), + randomCountryCode: () => faker.location.countryCode(), + randomLatitude: () => faker.location.latitude(), + randomLongitude: () => faker.location.longitude(), + randomAvatarImage: () => faker.image.avatar(), + randomImageUrl: () => faker.image.url(), + randomAbstractImage: () => faker.image.urlLoremFlickr({ category: 'abstract' }), + randomAnimalsImage: () => faker.image.urlLoremFlickr({ category: 'animals' }), + randomBusinessImage: () => faker.image.urlLoremFlickr({ category: 'business' }), + randomCatsImage: () => faker.image.urlLoremFlickr({ category: 'cats' }), + randomCityImage: () => faker.image.urlLoremFlickr({ category: 'city' }), + randomFoodImage: () => faker.image.urlLoremFlickr({ category: 'food' }), + randomNightlifeImage: () => faker.image.urlLoremFlickr({ category: 'nightlife' }), + randomFashionImage: () => faker.image.urlLoremFlickr({ category: 'fashion' }), + randomPeopleImage: () => faker.image.urlLoremFlickr({ category: 'people' }), + randomNatureImage: () => faker.image.urlLoremFlickr({ category: 'nature' }), + randomSportsImage: () => faker.image.urlLoremFlickr({ category: 'sports' }), + randomTransportImage: () => faker.image.urlLoremFlickr({ category: 'transport' }), + randomImageDataUri: () => faker.image.dataUri(), + randomBankAccount: () => faker.finance.accountNumber(), + randomBankAccountName: () => faker.finance.accountName(), + randomCreditCardMask: () => faker.finance.iban().replace(/(?<=.{4})\w(?=.{2})/g, '*'), + randomBankAccountBic: () => faker.finance.bic(), + randomBankAccountIban: () => faker.finance.iban(), + randomTransactionType: () => faker.finance.transactionType(), + randomCurrencyCode: () => faker.finance.currencyCode(), + randomCurrencyName: () => faker.finance.currencyName(), + randomCurrencySymbol: () => faker.finance.currencySymbol(), + randomBitcoin: () => faker.finance.bitcoinAddress(), + randomCompanyName: () => faker.company.name(), + randomCompanySuffix: () => faker.company.name(), + randomBs: () => faker.company.buzzPhrase(), + randomBsAdjective: () => faker.company.buzzAdjective(), + randomBsBuzz: () => faker.company.buzzVerb(), + randomBsNoun: () => faker.company.buzzNoun(), + randomCatchPhrase: () => faker.company.catchPhrase(), + randomCatchPhraseAdjective: () => faker.company.catchPhraseAdjective(), + randomCatchPhraseDescriptor: () => faker.company.catchPhraseDescriptor(), + randomCatchPhraseNoun: () => faker.company.catchPhraseNoun(), + randomDatabaseColumn: () => faker.database.column(), + randomDatabaseType: () => faker.database.type(), + randomDatabaseCollation: () => faker.database.collation(), + randomDatabaseEngine: () => faker.database.engine(), + randomDateFuture: () => faker.date.future().toISOString(), + randomDatePast: () => faker.date.past().toISOString(), + randomDateRecent: () => faker.date.recent().toISOString(), + randomWeekday: () => faker.date.weekday(), + randomMonth: () => faker.date.month(), + randomDomainName: () => faker.internet.domainName(), + randomDomainSuffix: () => faker.internet.domainSuffix(), + randomDomainWord: () => faker.internet.domainWord(), + randomEmail: () => faker.internet.email(), + randomExampleEmail: () => faker.internet.exampleEmail(), + randomUserName: () => faker.internet.username(), + randomUrl: () => faker.internet.url(), + randomFileName: () => faker.system.fileName(), + randomFileType: () => faker.system.fileType(), + randomFileExt: () => faker.system.fileExt(), + randomCommonFileName: () => faker.system.commonFileName(), + randomCommonFileType: () => faker.system.commonFileType(), + randomCommonFileExt: () => faker.system.commonFileExt(), + randomFilePath: () => faker.system.filePath(), + randomDirectoryPath: () => faker.system.directoryPath(), + randomMimeType: () => faker.system.mimeType(), + randomPrice: () => faker.commerce.price(), + randomProduct: () => faker.commerce.product(), + randomProductAdjective: () => faker.commerce.productAdjective(), + randomProductMaterial: () => faker.commerce.productMaterial(), + randomProductName: () => faker.commerce.productName(), + randomDepartment: () => faker.commerce.department(), + randomNoun: () => faker.hacker.noun(), + randomVerb: () => faker.hacker.verb(), + randomIngverb: () => faker.hacker.ingverb(), + randomAdjective: () => faker.hacker.adjective(), + randomWord: () => faker.hacker.noun(), + randomWords: () => faker.lorem.words(), + randomPhrase: () => faker.hacker.phrase(), + randomLoremWord: () => faker.lorem.word(), + randomLoremWords: () => faker.lorem.words(), + randomLoremSentence: () => faker.lorem.sentence(), + randomLoremSentences: () => faker.lorem.sentences(), + randomLoremParagraph: () => faker.lorem.paragraph(), + randomLoremParagraphs: () => faker.lorem.paragraphs(), + randomLoremText: () => faker.lorem.text(), + randomLoremSlug: () => faker.lorem.slug(), + randomLoremLines: () => faker.lorem.lines() +}; + +module.exports = { + mockDataFunctions +}; diff --git a/packages/bruno-electron/src/ipc/network/index.js b/packages/bruno-electron/src/ipc/network/index.js index ac417bf68..2fc32cba7 100644 --- a/packages/bruno-electron/src/ipc/network/index.js +++ b/packages/bruno-electron/src/ipc/network/index.js @@ -12,7 +12,6 @@ const { ipcMain } = require('electron'); const { each, get, extend, cloneDeep } = require('lodash'); const { NtlmClient } = require('axios-ntlm'); const { VarsRuntime, AssertRuntime, ScriptRuntime, TestRuntime } = require('@usebruno/js'); -const interpolateRandom = require('./interpolate-random'); const { interpolateString } = require('./interpolate-string'); const { resolveAwsV4Credentials, addAwsV4Interceptor } = require('./awsv4auth-helper'); const { addDigestInterceptor } = require('./digestauth-helper'); @@ -371,9 +370,6 @@ const registerNetworkIpc = (mainWindow) => { collection.globalEnvironmentVariables = scriptResult.globalEnvironmentVariables; } - // interpolate random/dynamic values inside request - interpolateRandom(request); - // interpolate variables inside request interpolateVars(request, envVars, runtimeVariables, processEnvVars); diff --git a/packages/bruno-electron/src/ipc/network/interpolate-random.js b/packages/bruno-electron/src/ipc/network/interpolate-random.js deleted file mode 100644 index 12beaecbc..000000000 --- a/packages/bruno-electron/src/ipc/network/interpolate-random.js +++ /dev/null @@ -1,196 +0,0 @@ -// Import faker.js -const { faker } = require('@faker-js/faker'); -const { each, forOwn } = require('lodash'); -const FormData = require('form-data'); - -const getContentType = (headers = {}) => { - let contentType = ''; - forOwn(headers, (value, key) => { - if (key && key.toLowerCase() === 'content-type') { - contentType = value; - } - }); - - return contentType; - }; - - const interpolateRandom = (request) => { - - const _interpolate = (str) => { - if (!str || !str.length || typeof str !== 'string') { - return str; - } - - let resultStr = str; - let matchFound = true; - - while (matchFound) { - const patternRegex = /\{\{\$([^}]+)\}\}/g; - matchFound = false; - resultStr = resultStr.replace(patternRegex, (_, placeholder) => { - const replacement = getRandomValue(placeholder); - matchFound = true; - return replacement; - }); - } - - return resultStr; - }; - - request.url = _interpolate(request.url); - - const contentType = getContentType(request.headers); - - if (contentType.includes('json')) { - if (typeof request.data === 'string') { - if (request.data.length) { - request.data = _interpolate(request.data); - } - } else if (typeof request.data === 'object') { - try { - let parsed = JSON.stringify(request.data); - parsed = _interpolate(parsed); - request.data = JSON.parse(parsed); - } catch (err) {} - } - } else if (contentType === 'application/x-www-form-urlencoded') { - if (typeof request.data === 'object') { - try { - forOwn(request?.data, (value, key) => { - request.data[key] = _interpolate(value); - }); - } catch (err) {} - } - } else if (contentType === 'multipart/form-data') { - if (typeof request.data === 'object' && !(request.data instanceof FormData)) { - try { - forOwn(request?.data, (value, key) => { - request.data[key] = _interpolate(value); - }); - } catch (err) {} - } - } else { - request.data = _interpolate(request.data); - } - - each(request.pathParams, (param) => { - param.value = _interpolate(param.value); - }); - - if (request?.pathParams?.length) { - let url = request.url; - - if (!url.startsWith('http://') && !url.startsWith('https://')) { - url = `http://${url}`; - } - - try { - url = new URL(url); - } catch (e) { - throw { message: 'Invalid URL format', originalError: e.message }; - } - - const urlPathnameInterpolatedWithPathParams = url.pathname - .split('/') - .filter((path) => path !== '') - .map((path) => { - if (path[0] !== ':') { - return '/' + path; - } else { - const name = path.slice(1); - const existingPathParam = request.pathParams.find((param) => param.type === 'path' && param.name === name); - return existingPathParam ? '/' + existingPathParam.value : ''; - } - }) - .join(''); - - const trailingSlash = url.pathname.endsWith('/') ? '/' : ''; - request.url = url.origin + urlPathnameInterpolatedWithPathParams + trailingSlash + url.search; - } - - return request; - }; - -// Function to get random values based on the variable name -function getRandomValue(variableName) { - switch(variableName) { - case "guid": - return faker.datatype.uuid(); // Generates a UUID - case "timestamp": - return Math.floor(Date.now() / 1000); // Current UNIX timestamp - case "isoTimestamp": - return new Date().toISOString(); // Current ISO timestamp - case "randomUUID": - return faker.string.uuid(); // Random 36-character UUID - case "randomAlphaNumeric": - return faker.string.alphanumeric(1); // Random alphanumeric character - case "randomBoolean": - return faker.datatype.boolean(); // Random boolean - case "randomInt": - return faker.number.int({ min: 0, max: 1000 }); // Random integer between 0 and 1000 - case "randomColor": - return faker.color.human(); // Random color name (human-readable) - case "randomHexColor": - return faker.color.rgb(); // Random hex color code - case "randomAbbreviation": - return faker.helpers.arrayElement(['SQL', 'PCI', 'JSON', 'HTTP', 'XML']); // Random abbreviation - case "randomIP": - return faker.network.ipv4(); // Random IPv4 address - case "randomIPV6": - return faker.network.ipv6(); // Random IPv6 address - case "randomMACAddress": - return faker.network.mac(); // Random MAC address - case "randomPassword": - return faker.internet.password(); // Random password (default 8 characters) - case "randomLocale": - return faker.locale; // Random two-letter language code (ISO 639-1) - case "randomUserAgent": - return faker.internet.userAgent(); // Random user agent string - case "randomProtocol": - return faker.internet.protocol(); // Random internet protocol (http, https) - case "randomSemver": - return faker.system.semver(); // Random semantic version - case "randomFirstName": - return faker.person.firstName(); // Random first name - case "randomLastName": - return faker.person.lastName(); // Random last name - case "randomFullName": - return faker.person.fullName(); // Random full name - case "randomNamePrefix": - return faker.person.prefix(); // Random name prefix (e.g., Mr., Ms.) - case "randomNameSuffix": - return faker.person.suffix(); // Random name suffix (e.g., MD, Jr.) - case "randomPhoneNumber": - return faker.phone.number(); // Random phone number - case "randomCity": - return faker.location.city(); // Random city name - case "randomStreetName": - return faker.location.streetName(); // Random street name - case "randomStreetAddress": - return faker.location.streetAddress(); // Random street address - case "randomCountry": - return faker.location.country(); // Random country name - case "randomCountryCode": - return faker.location.countryCode(); // Random two-letter country code - case "randomLatitude": - return faker.location.latitude(); // Random latitude coordinate - case "randomLongitude": - return faker.location.longitude(); // Random longitude coordinate - case "randomAvatarImage": - return faker.image.avatar(); // Random avatar image URL - case "randomImageUrl": - return faker.image.url(); // Random image URL - case "randomPrice": - return faker.commerce.price(); // Random price between 0.00 and 1000.00 - case "randomProduct": - return faker.commerce.product(); // Random product name - case "randomCompanyName": - return faker.company.name(); // Random company name - case "randomEmail": - return faker.internet.email(); // Random email address - default: - return null; // If no match found, return null - } -} - -module.exports = interpolateRandom; diff --git a/packages/bruno-electron/src/ipc/network/interpolate-vars.js b/packages/bruno-electron/src/ipc/network/interpolate-vars.js index abd856a58..932753fed 100644 --- a/packages/bruno-electron/src/ipc/network/interpolate-vars.js +++ b/packages/bruno-electron/src/ipc/network/interpolate-vars.js @@ -1,6 +1,7 @@ const { interpolate } = require('@usebruno/common'); const { each, forOwn, cloneDeep, find } = require('lodash'); const FormData = require('form-data'); +const { mockDataFunctions } = require('./faker-functions'); const getContentType = (headers = {}) => { let contentType = ''; @@ -13,6 +14,14 @@ const getContentType = (headers = {}) => { return contentType; }; +const interpolateMockVars = (str) => { + const patternRegex = /\{\{\$(\w+)\}\}/g; + return str.replace(patternRegex, (match, keyword) => { + const replacement = mockDataFunctions[keyword]?.(); + return replacement || match; + }); +}; + const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, processEnvVars = {}) => { const globalEnvironmentVariables = request?.globalEnvironmentVariables || {}; const oauth2CredentialVariables = request?.oauth2CredentialVariables || {}; @@ -55,7 +64,7 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc } }; - return interpolate(str, combinedVars); + return interpolateMockVars(interpolate(str, combinedVars)); }; request.url = _interpolate(request.url); @@ -239,7 +248,6 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc request.wsse.password = _interpolate(request.wsse.password) || ''; } - // interpolate vars for ntlmConfig auth if (request.ntlmConfig) { request.ntlmConfig.username = _interpolate(request.ntlmConfig.username) || '';