From 0b4e9e76404c4077c6250351c00cadb6189d4fdb Mon Sep 17 00:00:00 2001 From: Sanjai Kumar <84461672+sanjai0py@users.noreply.github.com> Date: Wed, 11 Sep 2024 10:52:11 +0530 Subject: [PATCH 01/59] Now the special characters in the search value are escapec before constructing the regexp. (#3057) --- packages/bruno-app/src/components/CodeEditor/index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/bruno-app/src/components/CodeEditor/index.js b/packages/bruno-app/src/components/CodeEditor/index.js index 0b12e68d..0b4187e5 100644 --- a/packages/bruno-app/src/components/CodeEditor/index.js +++ b/packages/bruno-app/src/components/CodeEditor/index.js @@ -6,7 +6,7 @@ */ import React from 'react'; -import isEqual from 'lodash/isEqual'; +import { isEqual, escapeRegExp } from 'lodash'; import { getEnvironmentVariables } from 'utils/collections'; import { defineCodeMirrorBrunoVariablesMode } from 'utils/common/codemirror'; import StyledWrapper from './StyledWrapper'; @@ -406,7 +406,8 @@ export default class CodeEditor extends React.Component { const searchInput = document.querySelector('.CodeMirror-search-field'); if (searchInput && searchInput.value.length > 0) { - const text = new RegExp(searchInput.value, 'gi'); + // Escape special characters in search input to prevent RegExp crashes. Fixes #3051 + const text = new RegExp(escapeRegExp(searchInput.value), 'gi'); const matches = this.editor.getValue().match(text); count = matches ? matches.length : 0; } From 087bab6fb49c424de5bd595bbe33a2e674b0d3f1 Mon Sep 17 00:00:00 2001 From: "Dawood F.K Morris" <22669106+DawoodMorris@users.noreply.github.com> Date: Wed, 11 Sep 2024 07:49:17 +0200 Subject: [PATCH 02/59] feat: display raw query response unformatted for readability (#2272) * feat: display raw query response unformatted for readability * chore: improved response json parsing --------- Co-authored-by: Anoop M D --- package-lock.json | 2 +- .../ResponsePane/QueryResult/index.js | 21 ++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 67bfa6c9..be8bc091 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18862,4 +18862,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/bruno-app/src/components/ResponsePane/QueryResult/index.js b/packages/bruno-app/src/components/ResponsePane/QueryResult/index.js index ee956f1b..da713a2c 100644 --- a/packages/bruno-app/src/components/ResponsePane/QueryResult/index.js +++ b/packages/bruno-app/src/components/ResponsePane/QueryResult/index.js @@ -19,11 +19,23 @@ const formatResponse = (data, mode, filter) => { } if (mode.includes('json')) { + let isValidJSON = false; + + try { + isValidJSON = typeof JSON.parse(JSON.stringify(data)) === 'object'; + } catch (error) { + console.log('Error parsing JSON: ', error.message); + } + + if (!isValidJSON || data === null) { + return data; + } + if (filter) { try { data = JSONPath({ path: filter, json: data }); } catch (e) { - console.warn('Could not filter with JSONPath.', e.message); + console.warn('Could not apply JSONPath filter:', e.message); } } @@ -35,15 +47,10 @@ const formatResponse = (data, mode, filter) => { if (typeof parsed === 'string') { return parsed; } - return safeStringifyJSON(parsed, true); } - if (typeof data === 'string') { - return data; - } - - return safeStringifyJSON(data); + return data; }; const QueryResult = ({ item, collection, data, dataBuffer, width, disableRunEventListener, headers, error }) => { From 98a7aa135711e8044224877d16c07a5a3cb0f0e2 Mon Sep 17 00:00:00 2001 From: Pragadesh-45 <54320162+Pragadesh-45@users.noreply.github.com> Date: Wed, 11 Sep 2024 11:25:34 +0530 Subject: [PATCH 03/59] fix: middle button click to close - SpecialTab and RequestTabNotFound tab (#3044) --- .../RequestTabs/RequestTab/index.js | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/bruno-app/src/components/RequestTabs/RequestTab/index.js b/packages/bruno-app/src/components/RequestTabs/RequestTab/index.js index 64d6eebb..83f6b72a 100644 --- a/packages/bruno-app/src/components/RequestTabs/RequestTab/index.js +++ b/packages/bruno-app/src/components/RequestTabs/RequestTab/index.js @@ -49,9 +49,10 @@ const RequestTab = ({ tab, collection, tabIndex, collectionRequestTabs, folderUi const handleMouseUp = (e) => { if (e.button === 1) { - e.stopPropagation(); e.preventDefault(); + e.stopPropagation(); + // Close the tab dispatch( closeTabs({ tabUids: [tab.uid] @@ -68,7 +69,10 @@ const RequestTab = ({ tab, collection, tabIndex, collectionRequestTabs, folderUi const folder = folderUid ? findItemInCollection(collection, folderUid) : null; if (['collection-settings', 'folder-settings', 'variables', 'collection-runner', 'security-settings'].includes(tab.type)) { return ( - + {tab.type === 'folder-settings' ? ( ) : ( @@ -82,7 +86,17 @@ const RequestTab = ({ tab, collection, tabIndex, collectionRequestTabs, folderUi if (!item) { return ( - + { + if (e.button === 1) { + e.preventDefault(); + e.stopPropagation(); + + dispatch(closeTabs({ tabUids: [tab.uid] })); + } + }} + > ); From 9a57f3870f6d8d2cb469392a6514cbe403dafb3f Mon Sep 17 00:00:00 2001 From: chrisn Date: Thu, 12 Sep 2024 08:03:06 +0200 Subject: [PATCH 04/59] fix: add hints of 'bru' object for 'getFolderVar' and 'getCollectionVar' methods (#3062) Co-authored-by: Chris Nagel --- packages/bruno-app/src/components/CodeEditor/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/bruno-app/src/components/CodeEditor/index.js b/packages/bruno-app/src/components/CodeEditor/index.js index 0b4187e5..df910389 100644 --- a/packages/bruno-app/src/components/CodeEditor/index.js +++ b/packages/bruno-app/src/components/CodeEditor/index.js @@ -61,6 +61,8 @@ if (!SERVER_RENDERED) { 'bru.getProcessEnv(key)', 'bru.hasEnvVar(key)', 'bru.getEnvVar(key)', + 'bru.getFolderVar(key)', + 'bru.getCollectionVar(key)', 'bru.setEnvVar(key,value)', 'bru.hasVar(key)', 'bru.getVar(key)', From 776866b3b475dc4170aea74076c6859c353acb09 Mon Sep 17 00:00:00 2001 From: Pragadesh-45 <54320162+Pragadesh-45@users.noreply.github.com> Date: Fri, 13 Sep 2024 11:12:38 +0530 Subject: [PATCH 05/59] style: added bottom border for `Table` component (#3078) --- packages/bruno-app/src/components/Table/StyledWrapper.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/bruno-app/src/components/Table/StyledWrapper.js b/packages/bruno-app/src/components/Table/StyledWrapper.js index e35f11b3..eeead4ed 100644 --- a/packages/bruno-app/src/components/Table/StyledWrapper.js +++ b/packages/bruno-app/src/components/Table/StyledWrapper.js @@ -27,6 +27,7 @@ const StyledWrapper = styled.div` table th { position: relative; + border-bottom: 1px solid ${(props) => props.theme.collection.environment.settings.gridBorder}77; } table tr td { From a08573f12098c1bac8ad1fc18e0265a8c2222a97 Mon Sep 17 00:00:00 2001 From: Pragadesh-45 <54320162+Pragadesh-45@users.noreply.github.com> Date: Fri, 13 Sep 2024 11:42:55 +0530 Subject: [PATCH 06/59] Feature/use focus trap (#3075) * addded tab order for modal elements * fixed Tab Order * feat: Add useFocusTrap hook for managing focus within modals * chore: improvements * chore: removed console log --------- Co-authored-by: Srikar Co-authored-by: srikary12 <121927567+srikary12@users.noreply.github.com> Co-authored-by: Anoop M D --- .../bruno-app/src/components/Modal/index.js | 15 +++++-- .../bruno-app/src/hooks/useFocusTrap/index.js | 43 +++++++++++++++++++ 2 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 packages/bruno-app/src/hooks/useFocusTrap/index.js diff --git a/packages/bruno-app/src/components/Modal/index.js b/packages/bruno-app/src/components/Modal/index.js index b83549aa..0ec1aca0 100644 --- a/packages/bruno-app/src/components/Modal/index.js +++ b/packages/bruno-app/src/components/Modal/index.js @@ -1,5 +1,6 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useRef } from 'react'; import StyledWrapper from './StyledWrapper'; +import useFocusTrap from 'hooks/useFocusTrap'; const ModalHeader = ({ title, handleCancel, customHeader, hideClose }) => (
@@ -69,6 +70,7 @@ const Modal = ({ onClick, closeModalFadeTimeout = 500 }) => { + const modalRef = useRef(null); const [isClosing, setIsClosing] = useState(false); const escFunction = (event) => { const escKeyCode = 27; @@ -77,6 +79,8 @@ const Modal = ({ } }; + useFocusTrap(modalRef); + const closeModal = (args) => { setIsClosing(true); setTimeout(() => handleCancel(args), closeModalFadeTimeout); @@ -85,7 +89,6 @@ const Modal = ({ useEffect(() => { if (disableEscapeKey) return; document.addEventListener('keydown', escFunction, false); - return () => { document.removeEventListener('keydown', escFunction, false); }; @@ -100,7 +103,13 @@ const Modal = ({ } return ( onClick(e) : null}> -
+
{ + const firstFocusableElementRef = useRef(null); + const lastFocusableElementRef = useRef(null); + + useEffect(() => { + const modalElement = modalRef.current; + if (!modalElement) return; + + const focusableElements = modalElement.querySelectorAll( + 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])' + ); + + if (focusableElements.length === 0) return; + + const firstElement = focusableElements[0]; + const lastElement = focusableElements[focusableElements.length - 1]; + + firstFocusableElementRef.current = firstElement; + lastFocusableElementRef.current = lastElement; + + const handleKeyDown = (event) => { + if (event.key === 'Tab') { + if (event.shiftKey && document.activeElement === firstElement) { + event.preventDefault(); + lastElement.focus(); + } else if (!event.shiftKey && document.activeElement === lastElement) { + event.preventDefault(); + firstElement.focus(); + } + } + }; + + modalElement.addEventListener('keydown', handleKeyDown); + + return () => { + modalElement.removeEventListener('keydown', handleKeyDown); + }; + }, [modalRef]); +}; + +export default useFocusTrap; From 81d8c30d8484678dc627b2a76d790c6c95faa1ea Mon Sep 17 00:00:00 2001 From: Sergei Karetnikov Date: Fri, 13 Sep 2024 08:18:05 +0200 Subject: [PATCH 07/59] feat(usebruno#2441): Set response body function (#3076) --- packages/bruno-js/src/bruno-response.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/bruno-js/src/bruno-response.js b/packages/bruno-js/src/bruno-response.js index 57e11a64..9e68045d 100644 --- a/packages/bruno-js/src/bruno-response.js +++ b/packages/bruno-js/src/bruno-response.js @@ -27,6 +27,15 @@ class BrunoResponse { getResponseTime() { return this.res ? this.res.responseTime : null; } + + setBody(data) { + if (!this.res) { + return; + } + + this.body = data; + this.res.data = data; + } } module.exports = BrunoResponse; From 9614ab069f786841d09222c76895e2b6062cc94f Mon Sep 17 00:00:00 2001 From: Harshmeet Singh <111004544+maxdiplogit@users.noreply.github.com> Date: Fri, 13 Sep 2024 11:52:22 +0530 Subject: [PATCH 08/59] fix: cursor jump to start (#3082) Co-authored-by: maxdiplogit --- .../src/components/RequestPane/QueryUrl/index.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js b/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js index bcbf55c9..e80ff8a3 100644 --- a/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js +++ b/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js @@ -25,7 +25,12 @@ const QueryUrl = ({ item, collection, handleRun }) => { setMethodSelectorWidth(el.offsetWidth); }, [method]); - const onSave = () => { + const onSave = (finalValue) => { + dispatch(requestUrlChanged({ + itemUid: item.uid, + collectionUid: collection.uid, + url: finalValue && typeof finalValue === 'string' ? finalValue.trim() : value + })); dispatch(saveRequest(item.uid, collection.uid)); }; @@ -34,7 +39,7 @@ const QueryUrl = ({ item, collection, handleRun }) => { requestUrlChanged({ itemUid: item.uid, collectionUid: collection.uid, - url: value && typeof value === 'string' ? value.trim() : value + url: (value && typeof value === 'string') && value }) ); }; @@ -64,7 +69,7 @@ const QueryUrl = ({ item, collection, handleRun }) => { > onSave(finalValue)} theme={storedTheme} onChange={(newValue) => onUrlChange(newValue)} onRun={handleRun} From 8856e8ec714fe1ffb0a5d15d8453607141a9a7bb Mon Sep 17 00:00:00 2001 From: Pragadesh-45 <54320162+Pragadesh-45@users.noreply.github.com> Date: Sun, 15 Sep 2024 00:08:35 +0530 Subject: [PATCH 09/59] fix cursor position restoration after `URL` trimming (#3087) --- .../components/RequestPane/QueryUrl/index.js | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js b/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js index e80ff8a3..15334d2f 100644 --- a/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js +++ b/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import get from 'lodash/get'; import { useDispatch } from 'react-redux'; import { requestUrlChanged, updateRequestMethod } from 'providers/ReduxStore/slices/collections'; @@ -17,6 +17,7 @@ const QueryUrl = ({ item, collection, handleRun }) => { const url = item.draft ? get(item, 'draft.request.url', '') : get(item, 'request.url', ''); const isMac = isMacOS(); const saveShortcut = isMac ? 'Cmd + S' : 'Ctrl + S'; + const editorRef = useRef(null); const [methodSelectorWidth, setMethodSelectorWidth] = useState(90); @@ -26,22 +27,32 @@ const QueryUrl = ({ item, collection, handleRun }) => { }, [method]); const onSave = (finalValue) => { - dispatch(requestUrlChanged({ - itemUid: item.uid, - collectionUid: collection.uid, - url: finalValue && typeof finalValue === 'string' ? finalValue.trim() : value - })); dispatch(saveRequest(item.uid, collection.uid)); }; const onUrlChange = (value) => { + if (!editorRef.current?.editor) return; + const editor = editorRef.current.editor; + const cursor = editor.getCursor(); + + const finalUrl = value?.trim() ?? value; + dispatch( requestUrlChanged({ itemUid: item.uid, collectionUid: collection.uid, - url: (value && typeof value === 'string') && value + url: finalUrl }) ); + + // Restore cursor position only if URL was trimmed + if (finalUrl !== value) { + setTimeout(() => { + if (editor) { + editor.setCursor(cursor); + } + }, 0); + } }; const onMethodSelect = (verb) => { @@ -68,6 +79,7 @@ const QueryUrl = ({ item, collection, handleRun }) => { }} > onSave(finalValue)} theme={storedTheme} From 0937bab7f50e1352fa36d0f4ab01319c83090de4 Mon Sep 17 00:00:00 2001 From: Sanjai Kumar <84461672+sanjai0py@users.noreply.github.com> Date: Sun, 15 Sep 2024 23:27:16 +0530 Subject: [PATCH 10/59] bugfix(#1320):Now the form-url-encoded params in the body can contain multiple values with same name. (#2964) * Now the form-url-encoded params in the body can contain multiple values with same name. * Updated the tests and renamed the function name * Added the inimported function * Minor changes. --- .../src/ipc/network/prepare-request.js | 12 +++--- packages/bruno-electron/src/utils/common.js | 21 +++++++++- .../tests/network/prepare-request.spec.js | 41 ++++++++++++++++++- 3 files changed, 67 insertions(+), 7 deletions(-) diff --git a/packages/bruno-electron/src/ipc/network/prepare-request.js b/packages/bruno-electron/src/ipc/network/prepare-request.js index 38f8f5d3..6601347b 100644 --- a/packages/bruno-electron/src/ipc/network/prepare-request.js +++ b/packages/bruno-electron/src/ipc/network/prepare-request.js @@ -5,6 +5,7 @@ const FormData = require('form-data'); const fs = require('fs'); const path = require('path'); const { getTreePathFromCollectionToItem } = require('../../utils/collection'); +const { buildFormUrlEncodedPayload } = require('../../utils/common'); const mergeFolderLevelHeaders = (request, requestTreePath) => { let folderHeaders = new Map(); @@ -300,7 +301,7 @@ const prepareRequest = (item, collection) => { let contentTypeDefined = false; let url = request.url; - // collection headers + // Collection level headers each(get(collectionRoot, 'request.headers', []), (h) => { if (h.enabled && h.name.length > 0) { headers[h.name] = h.value; @@ -319,6 +320,7 @@ const prepareRequest = (item, collection) => { mergeVars(collection, request, requestTreePath); } + // Request level headers each(request.headers, (h) => { if (h.enabled && h.name.length > 0) { headers[h.name] = h.value; @@ -372,11 +374,11 @@ const prepareRequest = (item, collection) => { } if (request.body.mode === 'formUrlEncoded') { - axiosRequest.headers['content-type'] = 'application/x-www-form-urlencoded'; - const params = {}; + if (!contentTypeDefined) { + axiosRequest.headers['content-type'] = 'application/x-www-form-urlencoded'; + } const enabledParams = filter(request.body.formUrlEncoded, (p) => p.enabled); - each(enabledParams, (p) => (params[p.name] = p.value)); - axiosRequest.data = params; + axiosRequest.data = buildFormUrlEncodedPayload(enabledParams); } if (request.body.mode === 'multipartForm') { diff --git a/packages/bruno-electron/src/utils/common.js b/packages/bruno-electron/src/utils/common.js index 50b17bb3..1962ac26 100644 --- a/packages/bruno-electron/src/utils/common.js +++ b/packages/bruno-electron/src/utils/common.js @@ -85,6 +85,24 @@ const flattenDataForDotNotation = (data) => { return result; }; +/** + * @param {Array.} params The request body Array + * @returns {object} Returns an obj with repeating key as a array of values + * {item: 2, item: 3, item1: 4} becomes {item: [2,3], item1: 4} + */ +const buildFormUrlEncodedPayload = (params) => { + return params.reduce((acc, p) => { + if (!acc[p.name]) { + acc[p.name] = p.value; + } else if (Array.isArray(acc[p.name])) { + acc[p.name].push(p.value); + } else { + acc[p.name] = [acc[p.name], p.value]; + } + return acc; + }, {}); +}; + module.exports = { uuid, stringifyJson, @@ -93,5 +111,6 @@ module.exports = { safeParseJSON, simpleHash, generateUidBasedOnHash, - flattenDataForDotNotation + flattenDataForDotNotation, + buildFormUrlEncodedPayload }; diff --git a/packages/bruno-electron/tests/network/prepare-request.spec.js b/packages/bruno-electron/tests/network/prepare-request.spec.js index 808a127d..b61d42c5 100644 --- a/packages/bruno-electron/tests/network/prepare-request.spec.js +++ b/packages/bruno-electron/tests/network/prepare-request.spec.js @@ -1,6 +1,7 @@ const { describe, it, expect } = require('@jest/globals'); const prepareRequest = require('../../src/ipc/network/prepare-request'); +const { buildFormUrlEncodedPayload } = require('../../src/utils/common'); describe('prepare-request: prepareRequest', () => { describe('Decomments request body', () => { @@ -17,5 +18,43 @@ describe('prepare-request: prepareRequest', () => { const result = prepareRequest({ request: { body } }, {}); expect(result.data).toEqual(expected); }); + + it('should handle single key-value pair', () => { + const requestObj = [{ name: 'item', value: 2 }]; + const expected = { item: 2 }; + const result = buildFormUrlEncodedPayload(requestObj); + expect(result).toEqual(expected); + }); + + it('should handle multiple key-value pairs with unique keys', () => { + const requestObj = [ + { name: 'item1', value: 2 }, + { name: 'item2', value: 3 } + ]; + const expected = { item1: 2, item2: 3 }; + const result = buildFormUrlEncodedPayload(requestObj); + expect(result).toEqual(expected); + }); + + it('should handle multiple key-value pairs with the same key', () => { + const requestObj = [ + { name: 'item', value: 2 }, + { name: 'item', value: 3 } + ]; + const expected = { item: [2, 3] }; + const result = buildFormUrlEncodedPayload(requestObj); + expect(result).toEqual(expected); + }); + + it('should handle mixed key-value pairs with unique and duplicate keys', () => { + const requestObj = [ + { name: 'item1', value: 2 }, + { name: 'item2', value: 3 }, + { name: 'item1', value: 4 } + ]; + const expected = { item1: [2, 4], item2: 3 }; + const result = buildFormUrlEncodedPayload(requestObj); + expect(result).toEqual(expected); + }); }); -}); +}); \ No newline at end of file From 0d3fde5efd6cb3e9a4a0264c034e0ae836e4b007 Mon Sep 17 00:00:00 2001 From: Anil Tallam Date: Sun, 15 Sep 2024 23:34:14 +0530 Subject: [PATCH 11/59] Handling request name conflicts while importing postman V2 collections (#1298) Issue: In Postman, multiple requests in same folder can have same name. current import code is creating bruneRequestItems with same name which is causing only one of the original requests to be actaully created. Looks like bruno doesn't allow multiple requests with same name in a given folder. Fix: Append _ to conflicting request names within same folder. --- .../src/utils/importers/postman-collection.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/bruno-app/src/utils/importers/postman-collection.js b/packages/bruno-app/src/utils/importers/postman-collection.js index 3f10aea9..b8128721 100644 --- a/packages/bruno-app/src/utils/importers/postman-collection.js +++ b/packages/bruno-app/src/utils/importers/postman-collection.js @@ -59,7 +59,8 @@ let translationLog = {}; const importPostmanV2CollectionItem = (brunoParent, item, parentAuth, options) => { brunoParent.items = brunoParent.items || []; const folderMap = {}; - + const requestMap = {}; + each(item, (i) => { if (isItemAFolder(i)) { const baseFolderName = i.name; @@ -84,6 +85,15 @@ const importPostmanV2CollectionItem = (brunoParent, item, parentAuth, options) = } } else { if (i.request) { + const baseRequestName = i.name; + let requestName = baseRequestName; + let count = 1; + + while (requestMap[requestName]) { + requestName = `${baseRequestName}_${count}`; + count++; + } + let url = ''; if (typeof i.request.url === 'string') { url = i.request.url; @@ -93,7 +103,7 @@ const importPostmanV2CollectionItem = (brunoParent, item, parentAuth, options) = const brunoRequestItem = { uid: uuid(), - name: i.name, + name: requestName, type: 'http-request', request: { url: url, @@ -308,6 +318,7 @@ const importPostmanV2CollectionItem = (brunoParent, item, parentAuth, options) = }); brunoParent.items.push(brunoRequestItem); + requestMap[requestName] = brunoRequestItem; } } }); From 83e505979c30a3abf0ff7a05466e00479cbbd299 Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Sun, 15 Sep 2024 23:37:14 +0530 Subject: [PATCH 12/59] chore: fixed typo during import of postman graphql collection --- packages/bruno-app/src/utils/importers/postman-collection.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bruno-app/src/utils/importers/postman-collection.js b/packages/bruno-app/src/utils/importers/postman-collection.js index b8128721..e9eaccf8 100644 --- a/packages/bruno-app/src/utils/importers/postman-collection.js +++ b/packages/bruno-app/src/utils/importers/postman-collection.js @@ -23,7 +23,7 @@ const parseGraphQLRequest = (graphqlSource) => { }; if (typeof graphqlSource === 'string') { - graphqlSource = JSON.parse(text); + graphqlSource = JSON.parse(graphqlSource); } if (graphqlSource.hasOwnProperty('variables') && graphqlSource.variables !== '') { From 3d5ae98e0447b58c25a0430a3bfddb351e6888d6 Mon Sep 17 00:00:00 2001 From: Sanjai Kumar <84461672+sanjai0py@users.noreply.github.com> Date: Sun, 15 Sep 2024 23:50:58 +0530 Subject: [PATCH 13/59] Improved the Environments modal with accessibility fixes (#2153) --- .../EnvironmentVariables/index.js | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js b/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js index 45a43a6a..8fcf4669 100644 --- a/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js +++ b/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js @@ -1,4 +1,6 @@ -import React from 'react'; +import React, { useRef, useEffect } from 'react'; +import toast from 'react-hot-toast'; +import cloneDeep from 'lodash/cloneDeep'; import { IconTrash } from '@tabler/icons'; import { useTheme } from 'providers/Theme'; import { useDispatch } from 'react-redux'; @@ -15,6 +17,7 @@ import toast from 'react-hot-toast'; const EnvironmentVariables = ({ environment, collection, setIsModified, originalEnvironmentVariables }) => { const dispatch = useDispatch(); const { storedTheme } = useTheme(); + const addButtonRef = useRef(null); const formik = useFormik({ enableReinitialize: true, @@ -85,6 +88,10 @@ const EnvironmentVariables = ({ environment, collection, setIsModified, original formik.setValues(formik.values.filter((variable) => variable.uid !== id)); }; + useEffect(() => { + addButtonRef.current?.scrollIntoView({ behavior: 'smooth' }); + }, [formik.values]); + const handleReset = () => { formik.resetForm({ originalEnvironmentVariables }); }; @@ -159,11 +166,15 @@ const EnvironmentVariables = ({ environment, collection, setIsModified, original ))} - -
- +
+ +
From d971aa959656025ab5c6cc4926de67615b2725c2 Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Sun, 15 Sep 2024 23:53:32 +0530 Subject: [PATCH 14/59] chore: fixed lint issue --- .../EnvironmentDetails/EnvironmentVariables/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js b/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js index 8fcf4669..a7ca95e4 100644 --- a/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js +++ b/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js @@ -11,7 +11,6 @@ import { useFormik } from 'formik'; import * as Yup from 'yup'; import { variableNameRegex } from 'utils/common/regex'; import { saveEnvironment } from 'providers/ReduxStore/slices/collections/actions'; -import cloneDeep from 'lodash/cloneDeep'; import toast from 'react-hot-toast'; const EnvironmentVariables = ({ environment, collection, setIsModified, originalEnvironmentVariables }) => { From 1057f54f2fbf65be0dbd139f51e2eb8fa72e1cb0 Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Sun, 15 Sep 2024 23:54:07 +0530 Subject: [PATCH 15/59] chore: fixed lint issue --- .../EnvironmentDetails/EnvironmentVariables/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js b/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js index a7ca95e4..fec0bc3e 100644 --- a/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js +++ b/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js @@ -1,5 +1,4 @@ import React, { useRef, useEffect } from 'react'; -import toast from 'react-hot-toast'; import cloneDeep from 'lodash/cloneDeep'; import { IconTrash } from '@tabler/icons'; import { useTheme } from 'providers/Theme'; From e680d0d71dc1cfc7b0060d3552827fcf0f407922 Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Sun, 15 Sep 2024 23:59:49 +0530 Subject: [PATCH 16/59] env variables view: scroll only when form is dirty --- .../EnvironmentDetails/EnvironmentVariables/index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js b/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js index fec0bc3e..05d9708b 100644 --- a/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js +++ b/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js @@ -87,8 +87,10 @@ const EnvironmentVariables = ({ environment, collection, setIsModified, original }; useEffect(() => { - addButtonRef.current?.scrollIntoView({ behavior: 'smooth' }); - }, [formik.values]); + if (formik.dirty) { + addButtonRef.current?.scrollIntoView({ behavior: 'smooth' }); + } + }, [formik.values, formik.dirty]); const handleReset = () => { formik.resetForm({ originalEnvironmentVariables }); From b3c72b1640b4fb2959750b28b7df16c1272c3dfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Linhart?= Date: Sun, 15 Sep 2024 20:33:45 +0200 Subject: [PATCH 17/59] bugfix/useragent-header (#2979) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add bruno-specific userAgent header * Update axios-instance.js --------- Co-authored-by: Linhart Lukáš Co-authored-by: Anoop M D --- packages/bruno-cli/src/utils/axios-instance.js | 2 ++ packages/bruno-electron/src/ipc/network/axios-instance.js | 3 +++ 2 files changed, 5 insertions(+) diff --git a/packages/bruno-cli/src/utils/axios-instance.js b/packages/bruno-cli/src/utils/axios-instance.js index 22515648..af96806e 100644 --- a/packages/bruno-cli/src/utils/axios-instance.js +++ b/packages/bruno-cli/src/utils/axios-instance.js @@ -1,4 +1,5 @@ const axios = require('axios'); +const { CLI_VERSION } = require('../constants'); /** * Function that configures axios with timing interceptors @@ -12,6 +13,7 @@ function makeAxiosInstance() { instance.interceptors.request.use((config) => { config.headers['request-start-time'] = Date.now(); + config.headers['user-agent'] = `bruno-runtime/${CLI_VERSION}`; return config; }); diff --git a/packages/bruno-electron/src/ipc/network/axios-instance.js b/packages/bruno-electron/src/ipc/network/axios-instance.js index a292973f..f4ae59ce 100644 --- a/packages/bruno-electron/src/ipc/network/axios-instance.js +++ b/packages/bruno-electron/src/ipc/network/axios-instance.js @@ -2,6 +2,7 @@ const URL = require('url'); const Socket = require('net').Socket; const axios = require('axios'); const connectionCache = new Map(); // Cache to store checkConnection() results +const electronApp = require("electron"); const LOCAL_IPV6 = '::1'; const LOCAL_IPV4 = '127.0.0.1'; @@ -65,6 +66,7 @@ function makeAxiosInstance() { }, proxy: false }); + const version = electronApp?.app?.getVersion()?.substring(1) ?? ""; instance.interceptors.request.use(async (config) => { const url = URL.parse(config.url); @@ -84,6 +86,7 @@ function makeAxiosInstance() { } config.headers['request-start-time'] = Date.now(); + config.headers['user-agent'] = `bruno-runtime/${version}`; return config; }); From 7dd639192cb3e9d724b2a88192fd0aca8dd28217 Mon Sep 17 00:00:00 2001 From: Huynh Tien Date: Mon, 16 Sep 2024 01:36:06 +0700 Subject: [PATCH 18/59] Support more languages in Generate Code (#2991) * generate languages for all targets * change target client name * add scroll bar * remove debug log --- .../GenerateCodeItem/StyledWrapper.js | 3 +- .../CollectionItem/GenerateCodeItem/index.js | 51 ++----------------- .../src/utils/codegenerator/targets.js | 23 +++++++++ 3 files changed, 28 insertions(+), 49 deletions(-) create mode 100644 packages/bruno-app/src/utils/codegenerator/targets.js diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/StyledWrapper.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/StyledWrapper.js index 635c545e..c866498e 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/StyledWrapper.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/StyledWrapper.js @@ -8,8 +8,9 @@ const StyledWrapper = styled.div` .generate-code-sidebar { background-color: ${(props) => props.theme.collection.environment.settings.sidebar.bg}; border-right: solid 1px ${(props) => props.theme.collection.environment.settings.sidebar.borderRight}; - min-height: 400px; + max-height: 90vh; height: 100%; + overflow-y: auto; } .generate-code-item { diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/index.js index 2b19d461..9d24df1f 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/index.js @@ -6,56 +6,11 @@ import { isValidUrl } from 'utils/url'; import { find, get } from 'lodash'; import { findEnvironmentInCollection } from 'utils/collections'; import { interpolateUrl, interpolateUrlPathParams } from 'utils/url/index'; - -const languages = [ - { - name: 'HTTP', - target: 'http', - client: 'http1.1' - }, - { - name: 'JavaScript-Fetch', - target: 'javascript', - client: 'fetch' - }, - { - name: 'Javascript-jQuery', - target: 'javascript', - client: 'jquery' - }, - { - name: 'Javascript-axios', - target: 'javascript', - client: 'axios' - }, - { - name: 'Python-Python3', - target: 'python', - client: 'python3' - }, - { - name: 'Python-Requests', - target: 'python', - client: 'requests' - }, - { - name: 'PHP', - target: 'php', - client: 'curl' - }, - { - name: 'Shell-curl', - target: 'shell', - client: 'curl' - }, - { - name: 'Shell-httpie', - target: 'shell', - client: 'httpie' - } -]; +import { getLanguages } from 'utils/codegenerator/targets'; const GenerateCodeItem = ({ collection, item, onClose }) => { + const languages = getLanguages(); + const environment = findEnvironmentInCollection(collection, collection.activeEnvironmentUid); let envVars = {}; if (environment) { diff --git a/packages/bruno-app/src/utils/codegenerator/targets.js b/packages/bruno-app/src/utils/codegenerator/targets.js new file mode 100644 index 00000000..6197d9b5 --- /dev/null +++ b/packages/bruno-app/src/utils/codegenerator/targets.js @@ -0,0 +1,23 @@ +import { targets } from 'httpsnippet'; + +export const getLanguages = () => { + const allLanguages = []; + for (const target of Object.values(targets)) { + const { key, title } = target.info; + const clients = Object.keys(target.clientsById); + const languages = + (clients.length === 1) + ? [{ + name: title, + target: key, + client: clients[0] + }] + : clients.map(client => ({ + name: `${title}-${client}`, + target: key, + client + })); + allLanguages.push(...languages); + } + return allLanguages; +}; \ No newline at end of file From 3dfb27d447f7bdba96ef4473eb1ebcd82392b99f Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Mon, 16 Sep 2024 00:12:36 +0530 Subject: [PATCH 19/59] feat: display shell code exporter at the top --- .../CollectionItem/GenerateCodeItem/StyledWrapper.js | 2 +- packages/bruno-app/src/utils/codegenerator/targets.js | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/StyledWrapper.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/StyledWrapper.js index c866498e..ca582a84 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/StyledWrapper.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/StyledWrapper.js @@ -8,7 +8,7 @@ const StyledWrapper = styled.div` .generate-code-sidebar { background-color: ${(props) => props.theme.collection.environment.settings.sidebar.bg}; border-right: solid 1px ${(props) => props.theme.collection.environment.settings.sidebar.borderRight}; - max-height: 90vh; + max-height: 80vh; height: 100%; overflow-y: auto; } diff --git a/packages/bruno-app/src/utils/codegenerator/targets.js b/packages/bruno-app/src/utils/codegenerator/targets.js index 6197d9b5..95b222db 100644 --- a/packages/bruno-app/src/utils/codegenerator/targets.js +++ b/packages/bruno-app/src/utils/codegenerator/targets.js @@ -18,6 +18,14 @@ export const getLanguages = () => { client })); allLanguages.push(...languages); + + // Move "Shell-curl" to the top of the array + const shellCurlIndex = allLanguages.findIndex(lang => lang.name === "Shell-curl"); + if (shellCurlIndex !== -1) { + const [shellCurl] = allLanguages.splice(shellCurlIndex, 1); + allLanguages.unshift(shellCurl); + } } + return allLanguages; }; \ No newline at end of file From f31c997fed8235dcc4f211e33175e36f4d03d058 Mon Sep 17 00:00:00 2001 From: anusreesubash <65728079+anusreesubash@users.noreply.github.com> Date: Mon, 16 Sep 2024 00:22:59 +0530 Subject: [PATCH 20/59] Bugfix/openapispec empty tag (#2935) * test: added test for self closing tags in xml-json parser * fix: allows import of openapispec with empty string as tags --------- Co-authored-by: Anusree Subash --- .../src/utils/importers/openapi-collection.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/bruno-app/src/utils/importers/openapi-collection.js b/packages/bruno-app/src/utils/importers/openapi-collection.js index eb2944cb..3bfe8394 100644 --- a/packages/bruno-app/src/utils/importers/openapi-collection.js +++ b/packages/bruno-app/src/utils/importers/openapi-collection.js @@ -283,11 +283,16 @@ const groupRequestsByTags = (requests) => { each(requests, (request) => { let tags = request.operationObject.tags || []; if (tags.length > 0) { - let tag = tags[0]; // take first tag - if (!_groups[tag]) { - _groups[tag] = []; + let tag = tags[0].trim(); // take first tag and trim whitespace + + if (tag) { + if (!_groups[tag]) { + _groups[tag] = []; + } + _groups[tag].push(request); + } else { + ungrouped.push(request); } - _groups[tag].push(request); } else { ungrouped.push(request); } From 721d0e1e49b93c8be10c2b48385abc2f168883da Mon Sep 17 00:00:00 2001 From: anusreesubash <65728079+anusreesubash@users.noreply.github.com> Date: Mon, 16 Sep 2024 00:33:15 +0530 Subject: [PATCH 21/59] Improved Request Count Calculation and UI Handling in RunCollectionItem (#2959) * Fix | properl calculates the request number for folder run * Chore|formatted document --------- Co-authored-by: Anusree Subash --- .../CollectionItem/RunCollectionItem/index.js | 72 +++++++++++-------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/index.js index 8c3da90a..4a81f59a 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/index.js @@ -23,43 +23,53 @@ const RunCollectionItem = ({ collection, item, onClose }) => { onClose(); }; - const runLength = item ? get(item, 'items.length', 0) : get(collection, 'items.length', 0); - const items = flattenItems(item ? item.items : collection.items); - const requestItems = items.filter((item) => item.type !== 'folder'); - const recursiveRunLength = requestItems.length; + const getRequestsCount = (items) => { + const requestTypes = ['http-request', 'graphql-request'] + return items.filter(req => requestTypes.includes(req.type)).length; + } + + const runLength = item ? getRequestsCount(item.items) : get(collection, 'items.length', 0); + const flattenedItems = flattenItems(item ? item.items : collection.items); + const recursiveRunLength = getRequestsCount(flattenedItems); return ( -
- Run - ({runLength} requests) -
-
This will only run the requests in this folder.
+ {!runLength && !recursiveRunLength ? ( +
No request found in this folder.
+ ) : ( +
+
+ Run + ({runLength} requests) +
+
This will only run the requests in this folder.
-
- Recursive Run - ({recursiveRunLength} requests) -
-
This will run all the requests in this folder and all its subfolders.
+
+ Recursive Run + ({recursiveRunLength} requests) +
+
This will run all the requests in this folder and all its subfolders.
-
- - - - - - - - - -
+
+ + + + + + + + + +
+
+ )}
); From ea9111748f61f92d4f6a3746157f5495f9f0f446 Mon Sep 17 00:00:00 2001 From: Ed Brannin Date: Sun, 15 Sep 2024 13:25:30 -0600 Subject: [PATCH 22/59] Refactor: Extract CloseTabIcon, DraftTabIcon (#1166) Co-authored-by: Anoop M D --- .../RequestTab/CloseTab/CloseTabIcon.js | 10 +++++++++ .../RequestTab/CloseTab/DraftTabIcon.js | 15 +++++++++++++ .../RequestTab/RequestTabNotFound.js | 8 ++----- .../RequestTabs/RequestTab/SpecialTab.js | 8 ++----- .../RequestTabs/RequestTab/index.js | 21 ++++--------------- 5 files changed, 33 insertions(+), 29 deletions(-) create mode 100644 packages/bruno-app/src/components/RequestTabs/RequestTab/CloseTab/CloseTabIcon.js create mode 100644 packages/bruno-app/src/components/RequestTabs/RequestTab/CloseTab/DraftTabIcon.js diff --git a/packages/bruno-app/src/components/RequestTabs/RequestTab/CloseTab/CloseTabIcon.js b/packages/bruno-app/src/components/RequestTabs/RequestTab/CloseTab/CloseTabIcon.js new file mode 100644 index 00000000..de2aa845 --- /dev/null +++ b/packages/bruno-app/src/components/RequestTabs/RequestTab/CloseTab/CloseTabIcon.js @@ -0,0 +1,10 @@ +const CloseTabIcon = () => ( + + + +); + +export default CloseTabIcon; diff --git a/packages/bruno-app/src/components/RequestTabs/RequestTab/CloseTab/DraftTabIcon.js b/packages/bruno-app/src/components/RequestTabs/RequestTab/CloseTab/DraftTabIcon.js new file mode 100644 index 00000000..ab097c53 --- /dev/null +++ b/packages/bruno-app/src/components/RequestTabs/RequestTab/CloseTab/DraftTabIcon.js @@ -0,0 +1,15 @@ +const DraftTabIcon = () => ( + + + +); + +export default DraftTabIcon; diff --git a/packages/bruno-app/src/components/RequestTabs/RequestTab/RequestTabNotFound.js b/packages/bruno-app/src/components/RequestTabs/RequestTab/RequestTabNotFound.js index 7bf45446..0f69311a 100644 --- a/packages/bruno-app/src/components/RequestTabs/RequestTab/RequestTabNotFound.js +++ b/packages/bruno-app/src/components/RequestTabs/RequestTab/RequestTabNotFound.js @@ -1,5 +1,6 @@ import React, { useState, useEffect } from 'react'; import { IconAlertTriangle } from '@tabler/icons'; +import CloseTabIcon from './CloseTab/CloseTabIcon'; const RequestTabNotFound = ({ handleCloseClick }) => { const [showErrorMessage, setShowErrorMessage] = useState(false); @@ -28,12 +29,7 @@ const RequestTabNotFound = ({ handleCloseClick }) => { ) : null}
handleCloseClick(e)}> - - - +
); diff --git a/packages/bruno-app/src/components/RequestTabs/RequestTab/SpecialTab.js b/packages/bruno-app/src/components/RequestTabs/RequestTab/SpecialTab.js index 8745cf07..fc5fba82 100644 --- a/packages/bruno-app/src/components/RequestTabs/RequestTab/SpecialTab.js +++ b/packages/bruno-app/src/components/RequestTabs/RequestTab/SpecialTab.js @@ -1,4 +1,5 @@ import React from 'react'; +import CloseTabIcon from './CloseTab/CloseTabIcon'; import { IconVariable, IconSettings, IconRun, IconFolder, IconShieldLock } from '@tabler/icons'; const SpecialTab = ({ handleCloseClick, type, tabName }) => { @@ -51,12 +52,7 @@ const SpecialTab = ({ handleCloseClick, type, tabName }) => { <>
{getTabInfo(type, tabName)}
handleCloseClick(e)}> - - - +
); diff --git a/packages/bruno-app/src/components/RequestTabs/RequestTab/index.js b/packages/bruno-app/src/components/RequestTabs/RequestTab/index.js index 83f6b72a..d326885a 100644 --- a/packages/bruno-app/src/components/RequestTabs/RequestTab/index.js +++ b/packages/bruno-app/src/components/RequestTabs/RequestTab/index.js @@ -15,6 +15,8 @@ import StyledWrapper from './StyledWrapper'; import Dropdown from 'components/Dropdown'; import CloneCollectionItem from 'components/Sidebar/Collections/Collection/CollectionItem/CloneCollectionItem/index'; import NewRequest from 'components/Sidebar/NewRequest/index'; +import CloseTabIcon from './CloseTab/CloseTabIcon'; +import DraftTabIcon from './CloseTab/DraftTabIcon'; const RequestTab = ({ tab, collection, tabIndex, collectionRequestTabs, folderUid }) => { const dispatch = useDispatch(); @@ -180,24 +182,9 @@ const RequestTab = ({ tab, collection, tabIndex, collectionRequestTabs, folderUi }} > {!item.draft ? ( - - - + ) : ( - - - + )} From 50d93bc249cbb063ccf8c8b71f0da4c386a5e5cb Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Mon, 16 Sep 2024 01:00:23 +0530 Subject: [PATCH 23/59] chore: reorganized folder structure for tab icons --- .../RequestTabs/RequestTab/{CloseTab => }/CloseTabIcon.js | 0 .../RequestTabs/RequestTab/{CloseTab => }/DraftTabIcon.js | 0 .../components/RequestTabs/RequestTab/RequestTabNotFound.js | 2 +- .../src/components/RequestTabs/RequestTab/SpecialTab.js | 2 +- .../bruno-app/src/components/RequestTabs/RequestTab/index.js | 4 ++-- 5 files changed, 4 insertions(+), 4 deletions(-) rename packages/bruno-app/src/components/RequestTabs/RequestTab/{CloseTab => }/CloseTabIcon.js (100%) rename packages/bruno-app/src/components/RequestTabs/RequestTab/{CloseTab => }/DraftTabIcon.js (100%) diff --git a/packages/bruno-app/src/components/RequestTabs/RequestTab/CloseTab/CloseTabIcon.js b/packages/bruno-app/src/components/RequestTabs/RequestTab/CloseTabIcon.js similarity index 100% rename from packages/bruno-app/src/components/RequestTabs/RequestTab/CloseTab/CloseTabIcon.js rename to packages/bruno-app/src/components/RequestTabs/RequestTab/CloseTabIcon.js diff --git a/packages/bruno-app/src/components/RequestTabs/RequestTab/CloseTab/DraftTabIcon.js b/packages/bruno-app/src/components/RequestTabs/RequestTab/DraftTabIcon.js similarity index 100% rename from packages/bruno-app/src/components/RequestTabs/RequestTab/CloseTab/DraftTabIcon.js rename to packages/bruno-app/src/components/RequestTabs/RequestTab/DraftTabIcon.js diff --git a/packages/bruno-app/src/components/RequestTabs/RequestTab/RequestTabNotFound.js b/packages/bruno-app/src/components/RequestTabs/RequestTab/RequestTabNotFound.js index 0f69311a..220f1220 100644 --- a/packages/bruno-app/src/components/RequestTabs/RequestTab/RequestTabNotFound.js +++ b/packages/bruno-app/src/components/RequestTabs/RequestTab/RequestTabNotFound.js @@ -1,6 +1,6 @@ import React, { useState, useEffect } from 'react'; import { IconAlertTriangle } from '@tabler/icons'; -import CloseTabIcon from './CloseTab/CloseTabIcon'; +import CloseTabIcon from './CloseTabIcon'; const RequestTabNotFound = ({ handleCloseClick }) => { const [showErrorMessage, setShowErrorMessage] = useState(false); diff --git a/packages/bruno-app/src/components/RequestTabs/RequestTab/SpecialTab.js b/packages/bruno-app/src/components/RequestTabs/RequestTab/SpecialTab.js index fc5fba82..c5d09faa 100644 --- a/packages/bruno-app/src/components/RequestTabs/RequestTab/SpecialTab.js +++ b/packages/bruno-app/src/components/RequestTabs/RequestTab/SpecialTab.js @@ -1,5 +1,5 @@ import React from 'react'; -import CloseTabIcon from './CloseTab/CloseTabIcon'; +import CloseTabIcon from './CloseTabIcon'; import { IconVariable, IconSettings, IconRun, IconFolder, IconShieldLock } from '@tabler/icons'; const SpecialTab = ({ handleCloseClick, type, tabName }) => { diff --git a/packages/bruno-app/src/components/RequestTabs/RequestTab/index.js b/packages/bruno-app/src/components/RequestTabs/RequestTab/index.js index d326885a..f03cd877 100644 --- a/packages/bruno-app/src/components/RequestTabs/RequestTab/index.js +++ b/packages/bruno-app/src/components/RequestTabs/RequestTab/index.js @@ -15,8 +15,8 @@ import StyledWrapper from './StyledWrapper'; import Dropdown from 'components/Dropdown'; import CloneCollectionItem from 'components/Sidebar/Collections/Collection/CollectionItem/CloneCollectionItem/index'; import NewRequest from 'components/Sidebar/NewRequest/index'; -import CloseTabIcon from './CloseTab/CloseTabIcon'; -import DraftTabIcon from './CloseTab/DraftTabIcon'; +import CloseTabIcon from './CloseTabIcon'; +import DraftTabIcon from './DraftTabIcon'; const RequestTab = ({ tab, collection, tabIndex, collectionRequestTabs, folderUid }) => { const dispatch = useDispatch(); From 0ddfca4c22195d4c030164f4aa2324c5917ec563 Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Mon, 16 Sep 2024 01:14:24 +0530 Subject: [PATCH 24/59] chore: removed cross referencing docs across locale specific docs --- docs/contributing/contributing_bn.md | 16 ---------------- docs/contributing/contributing_cn.md | 16 ---------------- docs/contributing/contributing_de.md | 16 ---------------- docs/contributing/contributing_es.md | 16 ---------------- docs/contributing/contributing_fr.md | 16 ---------------- docs/contributing/contributing_hi.md | 16 ---------------- docs/contributing/contributing_it.md | 16 ---------------- docs/contributing/contributing_ja.md | 16 ---------------- docs/contributing/contributing_kr.md | 16 ---------------- docs/contributing/contributing_pl.md | 16 ---------------- docs/contributing/contributing_pt_br.md | 16 ---------------- docs/contributing/contributing_ro.md | 16 ---------------- docs/contributing/contributing_ru.md | 16 ---------------- docs/contributing/contributing_tr.md | 16 ---------------- docs/contributing/contributing_ua.md | 16 ---------------- docs/contributing/contributing_zhtw.md | 16 ---------------- docs/publishing/publishing_bn.md | 10 ---------- docs/publishing/publishing_cn.md | 10 ---------- docs/publishing/publishing_de.md | 10 ---------- docs/publishing/publishing_fr.md | 10 ---------- docs/publishing/publishing_ja.md | 10 ---------- docs/publishing/publishing_pl.md | 10 ---------- docs/publishing/publishing_pt_br.md | 10 ---------- docs/publishing/publishing_ro.md | 10 ---------- docs/publishing/publishing_tr.md | 10 ---------- docs/publishing/publishing_zhtw.md | 10 ---------- 26 files changed, 356 deletions(-) diff --git a/docs/contributing/contributing_bn.md b/docs/contributing/contributing_bn.md index 9348919f..a5d765a1 100644 --- a/docs/contributing/contributing_bn.md +++ b/docs/contributing/contributing_bn.md @@ -1,20 +1,4 @@ [English](../../contributing.md) -| [Українська](./contributing_ua.md) -| [Русский](./contributing_ru.md) -| [Türkçe](./contributing_tr.md) -| [Deutsch](./contributing_de.md) -| [Français](./contributing_fr.md) -| [Português (BR)](./contributing_pt_br.md) -| [한국어](./contributing_kr.md) -| **বাংলা** -| [Español](./contributing_es.md) -| [Italiano](./contributing_it.md) -| [Română](./contributing_ro.md) -| [Polski](./contributing_pl.md) -| [简体中文](./contributing_cn.md) -| [正體中文](./contributing_zhtw.md) -| [日本語](./contributing_ja.md) -| [हिंदी](./contributing_hi.md) ## আসুন ব্রুনোকে আরও ভালো করি, একসাথে!! diff --git a/docs/contributing/contributing_cn.md b/docs/contributing/contributing_cn.md index f20d0ce7..f355feba 100644 --- a/docs/contributing/contributing_cn.md +++ b/docs/contributing/contributing_cn.md @@ -1,20 +1,4 @@ [English](../../contributing.md) -| [Українська](./contributing_ua.md) -| [Русский](./contributing_ru.md) -| [Türkçe](./contributing_tr.md) -| [Deutsch](./contributing_de.md) -| [Français](./contributing_fr.md) -| [Português (BR)](./contributing_pt_br.md) -| [한국어](./contributing_kr.md) -| [বাংলা](./contributing_bn.md) -| [Español](./contributing_es.md) -| [Italiano](./contributing_it.md) -| [Română](./contributing_ro.md) -| [Polski](./contributing_pl.md) -| **简体中文** -| [正體中文](./contributing_zhtw.md) -| [日本語](./contributing_ja.md) -| [हिंदी](./contributing_hi.md) ## 让我们一起改进 Bruno! diff --git a/docs/contributing/contributing_de.md b/docs/contributing/contributing_de.md index 2e11bdc7..2983e8d2 100644 --- a/docs/contributing/contributing_de.md +++ b/docs/contributing/contributing_de.md @@ -1,20 +1,4 @@ [English](../../contributing.md) -| [Українська](./contributing_ua.md) -| [Русский](./contributing_ru.md) -| [Türkçe](./contributing_tr.md) -| **Deutsch** -| [Français](./contributing_fr.md) -| [Português (BR)](./contributing_pt_br.md) -| [한국어](./contributing_kr.md) -| [বাংলা](./contributing_bn.md) -| [Español](./contributing_es.md) -| [Italiano](./contributing_it.md) -| [Română](./contributing_ro.md) -| [Polski](./contributing_pl.md) -| [简体中文](./contributing_cn.md) -| [正體中文](./contributing_zhtw.md) -| [日本語](./contributing_ja.md) -| [हिंदी](./contributing_hi.md) ## Lass uns Bruno noch besser machen, gemeinsam!! diff --git a/docs/contributing/contributing_es.md b/docs/contributing/contributing_es.md index d94cee03..f84090a1 100644 --- a/docs/contributing/contributing_es.md +++ b/docs/contributing/contributing_es.md @@ -1,20 +1,4 @@ [English](../../contributing.md) -| [Українська](./contributing_ua.md) -| [Русский](./contributing_ru.md) -| [Türkçe](./contributing_tr.md) -| [Deutsch](./contributing_de.md) -| [Français](./contributing_fr.md) -| [Português (BR)](./contributing_pt_br.md) -| [한국어](./contributing_kr.md) -| [বাংলা](./contributing_bn.md) -| **Español** -| [Italiano](./contributing_it.md) -| [Română](./contributing_ro.md) -| [Polski](./contributing_pl.md) -| [简体中文](./contributing_cn.md) -| [正體中文](./contributing_zhtw.md) -| [日本語](./contributing_ja.md) -| [हिंदी](./contributing_hi.md) ## ¡Juntos, hagamos a Bruno mejor! diff --git a/docs/contributing/contributing_fr.md b/docs/contributing/contributing_fr.md index 4016ab3d..9b0eacda 100644 --- a/docs/contributing/contributing_fr.md +++ b/docs/contributing/contributing_fr.md @@ -1,20 +1,4 @@ [English](../../contributing.md) -| [Українська](./contributing_ua.md) -| [Русский](./contributing_ru.md) -| [Türkçe](./contributing_tr.md) -| [Deutsch](./contributing_de.md) -| **Français** -| [Português (BR)](./contributing_pt_br.md) -| [한국어](./contributing_kr.md) -| [বাংলা](./contributing_bn.md) -| [Español](./contributing_es.md) -| [Italiano](./contributing_it.md) -| [Română](./contributing_ro.md) -| [Polski](./contributing_pl.md) -| [简体中文](./contributing_cn.md) -| [正體中文](./contributing_zhtw.md) -| [日本語](./contributing_ja.md) -| [हिंदी](./contributing_hi.md) ## Ensemble, améliorons Bruno ! diff --git a/docs/contributing/contributing_hi.md b/docs/contributing/contributing_hi.md index 71f0f503..38f39c5e 100644 --- a/docs/contributing/contributing_hi.md +++ b/docs/contributing/contributing_hi.md @@ -1,20 +1,4 @@ [English](../../contributing.md) -| [Українська](./contributing_ua.md) -| [Русский](./contributing_ru.md) -| [Türkçe](./contributing_tr.md) -| [Deutsch](./contributing_de.md) -| [Français](./contributing_fr.md) -| [Português (BR)](./contributing_pt_br.md) -| [한국어](./contributing_kr.md) -| [বাংলা](./contributing_bn.md) -| [Español](./contributing_es.md) -| [Italiano](./contributing_it.md) -| [Română](./contributing_ro.md) -| [Polski](./contributing_pl.md) -| [简体中文](./contributing_cn.md) -| [正體中文](./contributing_zhtw.md) -| [日本語](./contributing_ja.md) -| **हिंदी** ## आइए मिलकर Bruno को बेहतर बनाएं !! diff --git a/docs/contributing/contributing_it.md b/docs/contributing/contributing_it.md index 93dce3e3..18b7518f 100644 --- a/docs/contributing/contributing_it.md +++ b/docs/contributing/contributing_it.md @@ -1,20 +1,4 @@ [English](../../contributing.md) -| [Українська](./contributing_ua.md) -| [Русский](./contributing_ru.md) -| [Türkçe](./contributing_tr.md) -| [Deutsch](./contributing_de.md) -| [Français](./contributing_fr.md) -| [Português (BR)](./contributing_pt_br.md) -| [한국어](./contributing_kr.md) -| [বাংলা](./contributing_bn.md) -| [Español](./contributing_es.md) -| **Italiano** -| [Română](./contributing_ro.md) -| [Polski](./contributing_pl.md) -| [简体中文](./contributing_cn.md) -| [正體中文](./contributing_zhtw.md) -| [日本語](./contributing_ja.md) -| [हिंदी](./contributing_hi.md) ## Insieme, miglioriamo Bruno! diff --git a/docs/contributing/contributing_ja.md b/docs/contributing/contributing_ja.md index 604c5957..da219788 100644 --- a/docs/contributing/contributing_ja.md +++ b/docs/contributing/contributing_ja.md @@ -1,20 +1,4 @@ [English](../../contributing.md) -| [Українська](./contributing_ua.md) -| [Русский](./contributing_ru.md) -| [Türkçe](./contributing_tr.md) -| [Deutsch](./contributing_de.md) -| [Français](./contributing_fr.md) -| [Português (BR)](./contributing_pt_br.md) -| [한국어](./contributing_kr.md) -| [বাংলা](./contributing_bn.md) -| [Español](./contributing_es.md) -| [Italiano](./contributing_it.md) -| [Română](./contributing_ro.md) -| [Polski](./contributing_pl.md) -| [简体中文](./contributing_cn.md) -| [正體中文](./contributing_zhtw.md) -| **日本語** -| [हिंदी](./contributing_hi.md) ## 一緒に Bruno をよりよいものにしていきましょう!! diff --git a/docs/contributing/contributing_kr.md b/docs/contributing/contributing_kr.md index d1c00830..70e5a12f 100644 --- a/docs/contributing/contributing_kr.md +++ b/docs/contributing/contributing_kr.md @@ -1,20 +1,4 @@ [English](../../contributing.md) -| [Українська](./contributing_ua.md) -| [Русский](./contributing_ru.md) -| [Türkçe](./contributing_tr.md) -| [Deutsch](./contributing_de.md) -| [Français](./contributing_fr.md) -| [Português (BR)](./contributing_pt_br.md) -| **한국어** -| [বাংলা](./contributing_bn.md) -| [Español](./contributing_es.md) -| [Italiano](./contributing_it.md) -| [Română](./contributing_ro.md) -| [Polski](./contributing_pl.md) -| [简体中文](./contributing_cn.md) -| [正體中文](./contributing_zhtw.md) -| [日本語](./contributing_ja.md) -| [हिंदी](./contributing_hi.md) ## 함께 Bruno를 더 좋게 만들어요!! diff --git a/docs/contributing/contributing_pl.md b/docs/contributing/contributing_pl.md index 6cc7a7f5..085407db 100644 --- a/docs/contributing/contributing_pl.md +++ b/docs/contributing/contributing_pl.md @@ -1,20 +1,4 @@ [English](../../contributing.md) -| [Українська](./contributing_ua.md) -| [Русский](./contributing_ru.md) -| [Türkçe](./contributing_tr.md) -| [Deutsch](./contributing_de.md) -| [Français](./contributing_fr.md) -| [Português (BR)](./contributing_pt_br.md) -| [한국어](./contributing_kr.md) -| [বাংলা](./contributing_bn.md) -| [Español](./contributing_es.md) -| [Italiano](./contributing_it.md) -| [Română](./contributing_ro.md) -| **Polski** -| [简体中文](./contributing_cn.md) -| [正體中文](./contributing_zhtw.md) -| [日本語](./contributing_ja.md) -| [हिंदी](./contributing_hi.md) ## Wspólnie uczynijmy Bruno lepszym !! diff --git a/docs/contributing/contributing_pt_br.md b/docs/contributing/contributing_pt_br.md index 52bdecaf..513dfcc3 100644 --- a/docs/contributing/contributing_pt_br.md +++ b/docs/contributing/contributing_pt_br.md @@ -1,20 +1,4 @@ [English](../../contributing.md) -| [Українська](./contributing_ua.md) -| [Русский](./contributing_ru.md) -| [Türkçe](./contributing_tr.md) -| [Deutsch](./contributing_de.md) -| [Français](./contributing_fr.md) -| **Português (BR)** -| [한국어](./contributing_kr.md) -| [বাংলা](./contributing_bn.md) -| [Español](./contributing_es.md) -| [Italiano](./contributing_it.md) -| [Română](./contributing_ro.md) -| [Polski](./contributing_pl.md) -| [简体中文](./contributing_cn.md) -| [正體中文](./contributing_zhtw.md) -| [日本語](./contributing_ja.md) -| [हिंदी](./contributing_hi.md) ## Vamos tornar o Bruno melhor, juntos!! diff --git a/docs/contributing/contributing_ro.md b/docs/contributing/contributing_ro.md index 5a7e23f9..ee210261 100644 --- a/docs/contributing/contributing_ro.md +++ b/docs/contributing/contributing_ro.md @@ -1,20 +1,4 @@ [English](../../contributing.md) -| [Українська](./contributing_ua.md) -| [Русский](./contributing_ru.md) -| [Türkçe](./contributing_tr.md) -| [Deutsch](./contributing_de.md) -| [Français](./contributing_fr.md) -| [Português (BR)](./contributing_pt_br.md) -| [한국어](./contributing_kr.md) -| [বাংলা](./contributing_bn.md) -| [Español](./contributing_es.md) -| [Italiano](./contributing_it.md) -| **Română** -| [Polski](./contributing_pl.md) -| [简体中文](./contributing_cn.md) -| [正體中文](./contributing_zhtw.md) -| [日本語](./contributing_ja.md) -| [हिंदी](./contributing_hi.md) ## Haideţi să îmbunătățim Bruno, împreună!! diff --git a/docs/contributing/contributing_ru.md b/docs/contributing/contributing_ru.md index ce4cf16d..ab967953 100644 --- a/docs/contributing/contributing_ru.md +++ b/docs/contributing/contributing_ru.md @@ -1,20 +1,4 @@ [English](../../contributing.md) -| [Українська](./contributing_ua.md) -| **Русский** -| [Türkçe](./contributing_tr.md) -| [Deutsch](./contributing_de.md) -| [Français](./contributing_fr.md) -| [Português (BR)](./contributing_pt_br.md) -| [한국어](./contributing_kr.md) -| [বাংলা](./contributing_bn.md) -| [Español](./contributing_es.md) -| [Italiano](./contributing_it.md) -| [Română](./contributing_ro.md) -| [Polski](./contributing_pl.md) -| [简体中文](./contributing_cn.md) -| [正體中文](./contributing_zhtw.md) -| [日本語](./contributing_ja.md) -| [हिंदी](./contributing_hi.md) ## Давайте вместе сделаем Бруно лучше!!! diff --git a/docs/contributing/contributing_tr.md b/docs/contributing/contributing_tr.md index 2789b3a5..26531cab 100644 --- a/docs/contributing/contributing_tr.md +++ b/docs/contributing/contributing_tr.md @@ -1,20 +1,4 @@ [English](../../contributing.md) -| [Українська](./contributing_ua.md) -| [Русский](./contributing_ru.md) -| **Türkçe** -| [Deutsch](./contributing_de.md) -| [Français](./contributing_fr.md) -| [Português (BR)](./contributing_pt_br.md) -| [한국어](./contributing_kr.md) -| [বাংলা](./contributing_bn.md) -| [Español](./contributing_es.md) -| [Italiano](./contributing_it.md) -| [Română](./contributing_ro.md) -| [Polski](./contributing_pl.md) -| [简体中文](./contributing_cn.md) -| [正體中文](./contributing_zhtw.md) -| [日本語](./contributing_ja.md) -| [हिंदी](./contributing_hi.md) ## Bruno'yu birlikte daha iyi hale getirelim!!! diff --git a/docs/contributing/contributing_ua.md b/docs/contributing/contributing_ua.md index aeace255..88f9ab59 100644 --- a/docs/contributing/contributing_ua.md +++ b/docs/contributing/contributing_ua.md @@ -1,20 +1,4 @@ [English](../../contributing.md) -| **Українська** -| [Русский](./contributing_ru.md) -| [Türkçe](./contributing_tr.md) -| [Deutsch](./contributing_de.md) -| [Français](./contributing_fr.md) -| [Português (BR)](./contributing_pt_br.md) -| [한국어](./contributing_kr.md) -| [বাংলা](./contributing_bn.md) -| [Español](./contributing_es.md) -| [Italiano](./contributing_it.md) -| [Română](./contributing_ro.md) -| [Polski](./contributing_pl.md) -| [简体中文](./contributing_cn.md) -| [正體中文](./contributing_zhtw.md) -| [日本語](./contributing_ja.md) -| [हिंदी](./contributing_hi.md) ## Давайте зробимо Bruno краще, разом !! diff --git a/docs/contributing/contributing_zhtw.md b/docs/contributing/contributing_zhtw.md index 48ec8248..f061adb4 100644 --- a/docs/contributing/contributing_zhtw.md +++ b/docs/contributing/contributing_zhtw.md @@ -1,20 +1,4 @@ [English](../../contributing.md) -| [Українська](./contributing_ua.md) -| [Русский](./contributing_ru.md) -| [Türkçe](./contributing_tr.md) -| [Deutsch](./contributing_de.md) -| [Français](./contributing_fr.md) -| [Português (BR)](./contributing_pt_br.md) -| [한국어](./contributing_kr.md) -| [বাংলা](./contributing_bn.md) -| [Español](./contributing_es.md) -| [Italiano](./contributing_it.md) -| [Română](./contributing_ro.md) -| [Polski](./contributing_pl.md) -| [简体中文](./contributing_cn.md) -| **正體中文** -| [日本語](./contributing_ja.md) -| [हिंदी](./contributing_hi.md) ## 讓我們一起來讓 Bruno 變得更好! diff --git a/docs/publishing/publishing_bn.md b/docs/publishing/publishing_bn.md index 44cc55ae..bca792f7 100644 --- a/docs/publishing/publishing_bn.md +++ b/docs/publishing/publishing_bn.md @@ -1,14 +1,4 @@ [English](../../publishing.md) -| [Türkçe](./publishing_tr.md) -| [Deutsch](./publishing_de.md) -| [Français](./publishing_fr.md) -| [Português (BR)](./publishing_pt_br.md) -| **বাংলা** -| [Română](./publishing_ro.md) -| [Polski](./publishing_pl.md) -| [简体中文](./publishing_cn.md) -| [正體中文](./publishing_zhtw.md) -| [日本語](./publishing_ja.md) ### ব্রুনোকে নতুন প্যাকেজ ম্যানেজারে প্রকাশ করা diff --git a/docs/publishing/publishing_cn.md b/docs/publishing/publishing_cn.md index 04bd23c8..08835547 100644 --- a/docs/publishing/publishing_cn.md +++ b/docs/publishing/publishing_cn.md @@ -1,14 +1,4 @@ [English](../../publishing.md) -| [Türkçe](./publishing_tr.md) -| [Deutsch](./publishing_de.md) -| [Français](./publishing_fr.md) -| [Português (BR)](./publishing_pt_br.md) -| [বাংলা](./publishing_bn.md) -| [Română](./publishing_ro.md) -| [Polski](./publishing_pl.md) -| **简体中文** -| [正體中文](./publishing_zhtw.md) -| [日本語](./publishing_ja.md) ### 将 Bruno 发布到新的包管理器 diff --git a/docs/publishing/publishing_de.md b/docs/publishing/publishing_de.md index 01f866c0..e8388bc5 100644 --- a/docs/publishing/publishing_de.md +++ b/docs/publishing/publishing_de.md @@ -1,14 +1,4 @@ [English](../../publishing.md) -| [Türkçe](./publishing_tr.md) -| **Deutsch** -| [Français](./publishing_fr.md) -| [Português (BR)](./publishing_pt_br.md) -| [বাংলা](./publishing_bn.md) -| [Română](./publishing_ro.md) -| [Polski](./publishing_pl.md) -| [简体中文](./publishing_cn.md) -| [正體中文](./publishing_zhtw.md) -| [日本語](./publishing_ja.md) ### Veröffentlichung von Bruno über neue Paket-Manager diff --git a/docs/publishing/publishing_fr.md b/docs/publishing/publishing_fr.md index fd88a8dc..856665e2 100644 --- a/docs/publishing/publishing_fr.md +++ b/docs/publishing/publishing_fr.md @@ -1,14 +1,4 @@ [English](../../publishing.md) -| [Türkçe](./publishing_tr.md) -| [Deutsch](./publishing_de.md) -| **Français** -| [Português (BR)](./publishing_pt_br.md) -| [বাংলা](./publishing_bn.md) -| [Română](./publishing_ro.md) -| [Polski](./publishing_pl.md) -| [简体中文](./publishing_cn.md) -| [正體中文](./publishing_zhtw.md) -| [日本語](./publishing_ja.md) ### Publier Bruno dans un nouveau gestionnaire de paquets diff --git a/docs/publishing/publishing_ja.md b/docs/publishing/publishing_ja.md index adb3f120..be45b647 100644 --- a/docs/publishing/publishing_ja.md +++ b/docs/publishing/publishing_ja.md @@ -1,14 +1,4 @@ [English](../../publishing.md) -| [Türkçe](./publishing_tr.md) -| [Deutsch](./publishing_de.md) -| [Français](./publishing_fr.md) -| [Português (BR)](./publishing_pt_br.md) -| [বাংলা](./publishing_bn.md) -| [Română](./publishing_ro.md) -| [Polski](./publishing_pl.md) -| [简体中文](./publishing_cn.md) -| [正體中文](./publishing_zhtw.md) -| **日本語** ### Bruno を新しいパッケージマネージャに公開する場合の注意 diff --git a/docs/publishing/publishing_pl.md b/docs/publishing/publishing_pl.md index 37127c23..5fd8b0ac 100644 --- a/docs/publishing/publishing_pl.md +++ b/docs/publishing/publishing_pl.md @@ -1,14 +1,4 @@ [English](../../publishing.md) -| [Türkçe](./publishing_tr.md) -| [Deutsch](./publishing_de.md) -| [Français](./publishing_fr.md) -| [Português (BR)](./publishing_pt_br.md) -| [বাংলা](./publishing_bn.md) -| [Română](./publishing_ro.md) -| **Polski** -| [简体中文](./publishing_cn.md) -| [正體中文](./publishing_zhtw.md) -| [日本語](./publishing_ja.md) ### Publikowanie Bruno w nowym menedżerze pakietów diff --git a/docs/publishing/publishing_pt_br.md b/docs/publishing/publishing_pt_br.md index 3cb43cf5..7d49aca5 100644 --- a/docs/publishing/publishing_pt_br.md +++ b/docs/publishing/publishing_pt_br.md @@ -1,14 +1,4 @@ [English](../../publishing.md) -| [Türkçe](./publishing_tr.md) -| [Deutsch](./publishing_de.md) -| [Français](./publishing_fr.md) -| **Português (BR)** -| [বাংলা](./publishing_bn.md) -| [Română](./publishing_ro.md) -| [Polski](./publishing_pl.md) -| [简体中文](./publishing_cn.md) -| [正體中文](./publishing_zhtw.md) -| [日本語](./publishing_ja.md) ### Publicando Bruno em um novo gerenciador de pacotes diff --git a/docs/publishing/publishing_ro.md b/docs/publishing/publishing_ro.md index 44fcca7c..0c3177b0 100644 --- a/docs/publishing/publishing_ro.md +++ b/docs/publishing/publishing_ro.md @@ -1,14 +1,4 @@ [English](../../publishing.md) -| [Türkçe](./publishing_tr.md) -| [Deutsch](./publishing_de.md) -| [Français](./publishing_fr.md) -| [Português (BR)](./publishing_pt_br.md) -| [বাংলা](./publishing_bn.md) -| **Română** -| [Polski](./publishing_pl.md) -| [简体中文](./publishing_cn.md) -| [正體中文](./publishing_zhtw.md) -| [日本語](./publishing_ja.md) ### Publicarea lui Bruno la un gestionar de pachete nou diff --git a/docs/publishing/publishing_tr.md b/docs/publishing/publishing_tr.md index 5dfb8763..314471f6 100644 --- a/docs/publishing/publishing_tr.md +++ b/docs/publishing/publishing_tr.md @@ -1,14 +1,4 @@ [English](../../publishing.md) -| **Türkçe** -| [Deutsch](./publishing_de.md) -| [Français](./publishing_fr.md) -| [Português (BR)](./publishing_pt_br.md) -| [বাংলা](./publishing_bn.md) -| [Română](./publishing_ro.md) -| [Polski](./publishing_pl.md) -| [简体中文](./publishing_cn.md) -| [正體中文](./publishing_zhtw.md) -| [日本語](./publishing_ja.md) ### Bruno'yu yeni bir paket yöneticisine yayınlama diff --git a/docs/publishing/publishing_zhtw.md b/docs/publishing/publishing_zhtw.md index 87548c21..294f468c 100644 --- a/docs/publishing/publishing_zhtw.md +++ b/docs/publishing/publishing_zhtw.md @@ -1,14 +1,4 @@ [English](../../publishing.md) -| [Türkçe](./publishing_tr.md) -| [Deutsch](./publishing_de.md) -| [Français](./publishing_fr.md) -| [Português (BR)](./publishing_pt_br.md) -| [বাংলা](./publishing_bn.md) -| [Română](./publishing_ro.md) -| [Polski](./publishing_pl.md) -| [简体中文](./publishing_cn.md) -| **正體中文** -| [日本語](./publishing_ja.md) ### 將 Bruno 發佈到新的套件管理器 From 6d239929dad467137a1d8ab0e40cee174304903e Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Mon, 16 Sep 2024 01:21:23 +0530 Subject: [PATCH 25/59] docs: Dutch (NL) documenatation --- contributing.md | 1 + docs/contributing/contributing_nl.md | 82 ++++++++++++++ docs/publishing/publishin_nl.md | 7 ++ docs/readme/readme_nl.md | 157 +++++++++++++++++++++++++++ publishing.md | 1 + readme.md | 1 + 6 files changed, 249 insertions(+) create mode 100644 docs/contributing/contributing_nl.md create mode 100644 docs/publishing/publishin_nl.md create mode 100644 docs/readme/readme_nl.md diff --git a/contributing.md b/contributing.md index 3835c2cb..0b845066 100644 --- a/contributing.md +++ b/contributing.md @@ -15,6 +15,7 @@ | [正體中文](docs/contributing/contributing_zhtw.md) | [日本語](docs/contributing/contributing_ja.md) | [हिंदी](docs/contributing/contributing_hi.md) +| [Nederlands](docs/contributing/contributing_nl.md) ## Let's make Bruno better, together!! diff --git a/docs/contributing/contributing_nl.md b/docs/contributing/contributing_nl.md new file mode 100644 index 00000000..8c8d9402 --- /dev/null +++ b/docs/contributing/contributing_nl.md @@ -0,0 +1,82 @@ +[English](../../contributing.md) + +## Laten we Bruno samen beter maken !! + +We zijn blij dat je Bruno wilt verbeteren. Hieronder staan de richtlijnen om Bruno op je computer op te zetten. + +### Technologiestack + +Bruno is gebouwd met Next.js en React. We gebruiken ook Electron om een desktopversie te leveren (die lokale collecties ondersteunt). + +Bibliotheken die we gebruiken: + +- CSS - Tailwind +- Code Editors - Codemirror +- State Management - Redux +- Iconen - Tabler Icons +- Formulieren - formik +- Schema Validatie - Yup +- Request Client - axios +- Bestandsysteem Watcher - chokidar + +### Afhankelijkheden + +Je hebt [Node v18.x of de nieuwste LTS-versie](https://nodejs.org/en/) en npm 8.x nodig. We gebruiken npm workspaces in het project. + +## Ontwikkeling + +Bruno wordt ontwikkeld als een desktop-app. Je moet de app laden door de Next.js app in één terminal te draaien en daarna de Electron app in een andere terminal te draaien. + +### Lokale Ontwikkeling + +```bash +# gebruik voorgeschreven node versie +nvm use + +# installeer afhankelijkheden +npm i --legacy-peer-deps + +# build pakketten +npm run build:graphql-docs +npm run build:bruno-query +npm run build:bruno-common + +# draai next app (terminal 1) +npm run dev:web + +# draai electron app (terminal 2) +npm run dev:electron +``` + +### Problemen oplossen + +Je kunt een `Unsupported platform`-fout tegenkomen wanneer je `npm install` uitvoert. Om dit te verhelpen, moet je `node_modules` en `package-lock.json` verwijderen en `npm install` uitvoeren. Dit zou alle benodigde afhankelijkheden moeten installeren om de app te draaien. + +```shell +# Verwijder node_modules in subdirectories +find ./ -type d -name "node_modules" -print0 | while read -d $'\0' dir; do + rm -rf "$dir" +done + +# Verwijder package-lock in subdirectories +find . -type f -name "package-lock.json" -delete +``` + +### Testen + +```bash +# bruno-schema +npm test --workspace=packages/bruno-schema + +# bruno-lang +npm test --workspace=packages/bruno-lang +``` + +### Pull Requests indienen + +- Houd de PR's klein en gefocust op één ding +- Volg het formaat voor het aanmaken van branches + - feature/[feature naam]: Deze branch moet wijzigingen voor een specifieke functie bevatten + - Voorbeeld: feature/dark-mode + - bugfix/[bug naam]: Deze branch moet alleen bugfixes voor een specifieke bug bevatten + - Voorbeeld: bugfix/bug-1 \ No newline at end of file diff --git a/docs/publishing/publishin_nl.md b/docs/publishing/publishin_nl.md new file mode 100644 index 00000000..a6c74279 --- /dev/null +++ b/docs/publishing/publishin_nl.md @@ -0,0 +1,7 @@ +[English](../../publishing.md) + +### Bruno publiceren naar een nieuwe pakketbeheerder + +Hoewel onze code open source is en beschikbaar voor iedereen, verzoeken we je vriendelijk om contact met ons op te nemen voordat je publicatie overweegt op nieuwe pakketbeheerders. Als de maker van Bruno houd ik het handelsmerk `Bruno` voor dit project en wil ik het distributieproces beheren. Als je Bruno op een nieuwe pakketbeheerder wilt zien, dien dan een GitHub-issue in. + +Hoewel de meerderheid van onze functies gratis en open source zijn (die REST en GraphQL API's dekken), streven we ernaar een harmonieuze balans te vinden tussen open-source principes en duurzaamheid - https://github.com/usebruno/bruno/discussions/269 \ No newline at end of file diff --git a/docs/readme/readme_nl.md b/docs/readme/readme_nl.md new file mode 100644 index 00000000..809de08e --- /dev/null +++ b/docs/readme/readme_nl.md @@ -0,0 +1,157 @@ +
+ + +### Bruno - Open source IDE voor het verkennen en testen van API's. + +[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%bruno) +[![CI](https://github.com/usebruno/bruno/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/usebruno/bruno/actions/workflows/tests.yml) +[![Commit Activity](https://img.shields.io/github/commit-activity/m/usebruno/bruno)](https://github.com/usebruno/bruno/pulse) +[![X](https://img.shields.io/twitter/follow/use_bruno?style=social&logo=x)](https://twitter.com/use_bruno) +[![Website](https://img.shields.io/badge/Website-Visit-blue)](https://www.usebruno.com) +[![Download](https://img.shields.io/badge/Download-Latest-brightgreen)](https://www.usebruno.com/downloads) + +[English](../../readme.md) | [Українська](docs/readme/readme_ua.md) | [Русский](docs/readme/readme_ru.md) | [Türkçe](docs/readme/readme_tr.md) | [Deutsch](docs/readme/readme_de.md) | ** Nederlands ** | [Français](docs/readme/readme_fr.md) | [Português (BR)](docs/readme/readme_pt_br.md) | [한국어](docs/readme/readme_kr.md) | [বাংলা](docs/readme/readme_bn.md) | [Español](docs/readme/readme_es.md) | [Italiano](docs/readme/readme_it.md) | [Română](docs/readme/readme_ro.md) | [Polski](docs/readme/readme_pl.md) | [简体中文](docs/readme/readme_cn.md) | [正體中文](docs/readme/readme_zhtw.md) | [العربية](docs/readme/readme_ar.md) | [日本語](docs/readme/readme_ja.md) + +Bruno is een nieuwe en innovatieve API-client, gericht op het revolutioneren van de status quo die wordt vertegenwoordigd door Postman en vergelijkbare tools. + +Bruno slaat je collecties direct op in een map op je bestandssysteem. We gebruiken een platte tekst opmaaktaal, Bru, om informatie over API-verzoeken op te slaan. + +Je kunt Git of elke versiebeheertool naar keuze gebruiken om samen te werken aan je API-collecties. + +Bruno is uitsluitend offline. Er zijn geen plannen om ooit cloud-synchronisatie aan Bruno toe te voegen. We waarderen je gegevensprivacy en geloven dat deze op je apparaat moet blijven. Lees onze langetermijnvisie [hier](https://github.com/usebruno/bruno/discussions/269) + +[Download Bruno](https://www.usebruno.com/downloads) + +📢 Bekijk onze recente presentatie op de India FOSS 3.0 Conference [hier](https://www.youtube.com/watch?v=7bSMFpbcPiY) + +![bruno](/assets/images/landing-2.png)

+ +### Golden Edition ✨ + +De meeste van onze functies zijn gratis en open source. +We streven naar een harmonieuze balans tussen [open-source principes en duurzaamheid](https://github.com/usebruno/bruno/discussions/269). + +Je kunt de [Golden Edition](https://www.usebruno.com/pricing) kopen voor een eenmalige betaling van **$19**!
+ +### Installatie + +Bruno is beschikbaar als binaire download [op onze website](https://www.usebruno.com/downloads) voor Mac, Windows en Linux. + +Je kunt Bruno ook installeren via pakketbeheerders zoals Homebrew, Chocolatey, Scoop, Snap, Flatpak en Apt. + +```sh +# Op Mac via Homebrew +brew install bruno + +# Op Windows via Chocolatey +choco install bruno + +# Op Windows via Scoop +scoop bucket add extras +scoop install bruno + +# Op Windows via winget +winget install Bruno.Bruno + +# Op Linux via Snap +snap install bruno + +# Op Linux via Flatpak +flatpak install com.usebruno.Bruno + +# Op Linux via Apt +sudo mkdir -p /etc/apt/keyrings +sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/bruno.gpg --keyserver keyserver.ubuntu.com --recv-keys 9FA6017ECABE0266 + +echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list + +sudo apt update +sudo apt install bruno +``` + +### Draai op meerdere platformen 🖥️ + +![bruno](/assets/images/run-anywhere.png)

+ +### Samenwerken via Git 👩‍💻🧑‍💻 + +Of elk versiebeheersysteem naar keuze + +![bruno](/assets/images/version-control.png)

+ +### Sponsors + +#### Gouden Sponsors + + + +#### Zilveren Sponsors + + + +#### Bronzen Sponsors + + + + + +### Belangrijke Links 📌 + +- [Onze Langetermijnvisie](https://github.com/usebruno/bruno/discussions/269) +- [Roadmap](https://github.com/usebruno/bruno/discussions/384) +- [Documentatie](https://docs.usebruno.com) +- [Stack Overflow](https://stackoverflow.com/questions/tagged/bruno) +- [Website](https://www.usebruno.com) +- [Prijzen](https://www.usebruno.com/pricing) +- [Download](https://www.usebruno.com/downloads) +- [GitHub Sponsors](https://github.com/sponsors/helloanoop) + +### Showcase 🎥 + +- [Getuigenissen](https://github.com/usebruno/bruno/discussions/343) +- [Kenniscentrum](https://github.com/usebruno/bruno/discussions/386) +- [Scriptmania](https://github.com/usebruno/bruno/discussions/385) + +### Ondersteuning ❤️ + +Als je Bruno leuk vindt en ons open-source werk wilt ondersteunen, overweeg dan om ons te sponsoren via [GitHub Sponsors](https://github.com/sponsors/helloanoop). + +### Deel Getuigenissen 📣 + +Als Bruno je heeft geholpen op je werk en in je teams, deel dan je [getuigenissen op onze GitHub-discussie](https://github.com/usebruno/bruno/discussions/343). + + +### Blijf in contact 🌐 + +[𝕏 (Twitter)](https://twitter.com/use_bruno)
+[Website](https://www.usebruno.com)
+[Discord](https://discord.com/invite/KgcZUncpjq)
+[LinkedIn](https://www.linkedin.com/company/usebruno) + +### Handelsmerk + +**Naam** + +`Bruno` is een handelsmerk in bezit van [Anoop M D](https://www.helloanoop.com/). + +**Logo** + +Het logo is afkomstig van [OpenMoji](https://openmoji.org/library/emoji-1F436/). Licentie: CC [BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) + +### Bijdragen 👩‍💻🧑‍💻 + +Ik ben blij dat je Bruno wilt verbeteren. Bekijk de [bijdragegids](contributing.md). + +Zelfs als je geen bijdragen via code kunt leveren, aarzel dan niet om bugs en functieverzoeken in te dienen die moeten worden geïmplementeerd om jouw gebruiksscenario op te lossen. + +### Auteurs + + + +### Licentie 📄 + +[MIT](../../license.md) \ No newline at end of file diff --git a/publishing.md b/publishing.md index 632fe18c..458077b2 100644 --- a/publishing.md +++ b/publishing.md @@ -9,6 +9,7 @@ | [简体中文](docs/publishing/publishing_cn.md) | [正體中文](docs/publishing/publishing_zhtw.md) | [日本語](docs/publishing/publishing_ja.md) +| [Nederlands](docs/publishing/publishing_nl.md) ### Publishing Bruno to a new package manager diff --git a/readme.md b/readme.md index 045be605..8b507ab7 100644 --- a/readme.md +++ b/readme.md @@ -28,6 +28,7 @@ | [العربية](docs/readme/readme_ar.md) | [日本語](docs/readme/readme_ja.md) | [ქართული](docs/readme/readme_ka.md) +| [Nederlands](docs/readme/readme_nl.md) Bruno is a new and innovative API client, aimed at revolutionizing the status quo represented by Postman and similar tools out there. From 19501812fc899cb40705894f06284f780d7991c9 Mon Sep 17 00:00:00 2001 From: Apoorv Yadav <32174554+apoorvyadav1111@users.noreply.github.com> Date: Sun, 15 Sep 2024 15:54:51 -0400 Subject: [PATCH 26/59] handle multiple env vars (#3107) Co-authored-by: ayadav16 --- packages/bruno-app/src/utils/url/index.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/bruno-app/src/utils/url/index.js b/packages/bruno-app/src/utils/url/index.js index f9557b3c..8785df00 100644 --- a/packages/bruno-app/src/utils/url/index.js +++ b/packages/bruno-app/src/utils/url/index.js @@ -42,15 +42,15 @@ export const parsePathParams = (url) => { uri = `http://${uri}`; } + let paths; + try { uri = new URL(uri); + paths = uri.pathname.split('/'); } catch (e) { - // URL is non-parsable, is it incomplete? Ignore. - return []; + paths = uri.split('/'); } - let paths = uri.pathname.split('/'); - paths = paths.reduce((acc, path) => { if (path !== '' && path[0] === ':') { let name = path.slice(1, path.length); @@ -63,7 +63,6 @@ export const parsePathParams = (url) => { } return acc; }, []); - return paths; }; From 4419634db755c028c9638f45b517b02faebbd81b Mon Sep 17 00:00:00 2001 From: juprem <134919753+juprem@users.noreply.github.com> Date: Sun, 15 Sep 2024 22:05:44 +0200 Subject: [PATCH 27/59] feat(#1222): trigger modal's handleConfirm on ENTER key down (#1223) * feat(#1222): trigger modal's handleConfirm on ENTER key down * Update index.js --------- Co-authored-by: Anoop M D --- .../bruno-app/src/components/Modal/index.js | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/packages/bruno-app/src/components/Modal/index.js b/packages/bruno-app/src/components/Modal/index.js index 0ec1aca0..413856ea 100644 --- a/packages/bruno-app/src/components/Modal/index.js +++ b/packages/bruno-app/src/components/Modal/index.js @@ -2,6 +2,9 @@ import React, { useEffect, useState, useRef } from 'react'; import StyledWrapper from './StyledWrapper'; import useFocusTrap from 'hooks/useFocusTrap'; +const ESC_KEY_CODE = 27; +const ENTER_KEY_CODE = 13; + const ModalHeader = ({ title, handleCancel, customHeader, hideClose }) => (
{customHeader ? customHeader : <>{title ?
{title}
: null}} @@ -72,10 +75,17 @@ const Modal = ({ }) => { const modalRef = useRef(null); const [isClosing, setIsClosing] = useState(false); - const escFunction = (event) => { - const escKeyCode = 27; - if (event.keyCode === escKeyCode) { - closeModal({ type: 'esc' }); + + const handleKeydown = ({ keyCode }) => { + switch (keyCode) { + case ESC_KEY_CODE: { + return closeModal({ type: 'esc' }); + } + case ENTER_KEY_CODE: { + if(handleConfirm) { + return handleConfirm(); + } + } } }; @@ -88,9 +98,9 @@ const Modal = ({ useEffect(() => { if (disableEscapeKey) return; - document.addEventListener('keydown', escFunction, false); + document.addEventListener('keydown', handleKeydown, false); return () => { - document.removeEventListener('keydown', escFunction, false); + document.removeEventListener('keydown', handleKeydown); }; }, [disableEscapeKey, document]); From 5e4a96792e6228e01cdc2ae7c373663de3d6a02b Mon Sep 17 00:00:00 2001 From: David Francis Date: Mon, 16 Sep 2024 04:03:18 -0700 Subject: [PATCH 28/59] feat: Feature/cli support multiple reporters (#2911) * Support multiple reporters at once in the CLI * Typos * Better logging string after writing file * Remove double blank line * More double blank lines * Switch reporter schema to one from discussion * Typo * Add comment --- packages/bruno-cli/src/commands/run.js | 85 +++++++++++++++++++++----- 1 file changed, 70 insertions(+), 15 deletions(-) diff --git a/packages/bruno-cli/src/commands/run.js b/packages/bruno-cli/src/commands/run.js index 53871bc5..58b3cdf8 100644 --- a/packages/bruno-cli/src/commands/run.js +++ b/packages/bruno-cli/src/commands/run.js @@ -235,6 +235,18 @@ const builder = async (yargs) => { default: 'json', type: 'string' }) + .option('reporter-json', { + describe: 'Path to write json file results to', + type: 'string' + }) + .option('reporter-junit', { + describe: 'Path to write junit file results to', + type: 'string' + }) + .option('reporter-html', { + describe: 'Path to write html file results to', + type: 'string' + }) .option('insecure', { type: 'boolean', description: 'Allow insecure server connections' @@ -267,6 +279,10 @@ const builder = async (yargs) => { '$0 run request.bru --output results.html --format html', 'Run a request and write the results to results.html in html format in the current directory' ) + .example( + '$0 run request.bru --reporter-junit results.xml --reporter-html results.html', + 'Run a request and write the results to results.html in html format and results.xml in junit format in the current directory' + ) .example('$0 run request.bru --tests-only', 'Run all requests that have a test') .example( @@ -291,6 +307,9 @@ const handler = async function (argv) { r: recursive, output: outputPath, format, + reporterJson, + reporterJunit, + reporterHtml, sandbox, testsOnly, bail @@ -392,6 +411,25 @@ const handler = async function (argv) { process.exit(constants.EXIT_STATUS.ERROR_INCORRECT_OUTPUT_FORMAT); } + let formats = {}; + + // Maintains back compat with --format and --output + if (outputPath && outputPath.length) { + formats[format] = outputPath; + } + + if (reporterHtml && reporterHtml.length) { + formats['html'] = reporterHtml; + } + + if (reporterJson && reporterJson.length) { + formats['json'] = reporterJson; + } + + if (reporterJunit && reporterJunit.length) { + formats['junit'] = reporterJunit; + } + // load .env file at root of collection if it exists const dotEnvPath = path.join(collectionPath, '.env'); const dotEnvExists = await exists(dotEnvPath); @@ -524,28 +562,45 @@ const handler = async function (argv) { const totalTime = results.reduce((acc, res) => acc + res.response.responseTime, 0); console.log(chalk.dim(chalk.grey(`Ran all requests - ${totalTime} ms`))); - if (outputPath && outputPath.length) { - const outputDir = path.dirname(outputPath); - const outputDirExists = await exists(outputDir); - if (!outputDirExists) { - console.error(chalk.red(`Output directory ${outputDir} does not exist`)); - process.exit(constants.EXIT_STATUS.ERROR_MISSING_OUTPUT_DIR); - } - + const formatKeys = Object.keys(formats); + if (formatKeys && formatKeys.length > 0) { const outputJson = { summary, results }; - if (format === 'json') { - fs.writeFileSync(outputPath, JSON.stringify(outputJson, null, 2)); - } else if (format === 'junit') { - makeJUnitOutput(results, outputPath); - } else if (format === 'html') { - makeHtmlOutput(outputJson, outputPath); + const reporters = { + 'json': (path) => fs.writeFileSync(path, JSON.stringify(outputJson, null, 2)), + 'junit': (path) => makeJUnitOutput(results, path), + 'html': (path) => makeHtmlOutput(outputJson, path), } - console.log(chalk.dim(chalk.grey(`Wrote results to ${outputPath}`))); + for (const formatter of Object.keys(formats)) + { + const reportPath = formats[formatter]; + const reporter = reporters[formatter]; + + // Skip formatters lacking an output path. + if (!reportPath || reportPath.length === 0) { + continue; + } + + const outputDir = path.dirname(reportPath); + const outputDirExists = await exists(outputDir); + if (!outputDirExists) { + console.error(chalk.red(`Output directory ${outputDir} does not exist`)); + process.exit(constants.EXIT_STATUS.ERROR_MISSING_OUTPUT_DIR); + } + + if (!reporter) { + console.error(chalk.red(`Reporter ${formatter} does not exist`)); + process.exit(constants.EXIT_STATUS.ERROR_INCORRECT_OUTPUT_FORMAT); + } + + reporter(reportPath); + + console.log(chalk.dim(chalk.grey(`Wrote ${formatter} results to ${reportPath}`))); + } } if (summary.failedAssertions + summary.failedTests + summary.failedRequests > 0) { From 238c790f9b0e2f84ea644123d308a18125d2b2c9 Mon Sep 17 00:00:00 2001 From: Shishir Karanth Date: Mon, 16 Sep 2024 16:40:29 +0530 Subject: [PATCH 29/59] fix: handle functions while marshalling (#3102) * fix: handle functions while marshalling * fix: js style --- packages/bruno-js/src/sandbox/quickjs/utils/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/bruno-js/src/sandbox/quickjs/utils/index.js b/packages/bruno-js/src/sandbox/quickjs/utils/index.js index e376c325..47be92f5 100644 --- a/packages/bruno-js/src/sandbox/quickjs/utils/index.js +++ b/packages/bruno-js/src/sandbox/quickjs/utils/index.js @@ -25,6 +25,8 @@ const marshallToVm = (value, vm) => { } return obj; } + } else if (typeof value === 'function') { + return vm.newString('[Function (anonymous)]'); } }; From 4f7cefe41da875c4849083f5a41809ee8082bf40 Mon Sep 17 00:00:00 2001 From: lohit Date: Mon, 16 Sep 2024 16:46:47 +0530 Subject: [PATCH 30/59] Feat/support for multiple preview modes of same response type (#2606) * pr review changes * collection root object in export json * import environment updates * tests run execution order fix for collection runs * support for multiple preview modes of same type --- .../QueryResult/QueryResultPreview/index.js | 4 ++-- .../ResponsePane/QueryResult/index.js | 24 +++++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js b/packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js index 86d79c4b..ff9c890f 100644 --- a/packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js +++ b/packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js @@ -29,7 +29,7 @@ const QueryResultPreview = ({ setNumPages(numPages); } // Fail safe, so we don't render anything with an invalid tab - if (!allowedPreviewModes.includes(previewTab)) { + if (!allowedPreviewModes.find((previewMode) => previewMode?.uid == previewTab?.uid)) { return null; } @@ -40,7 +40,7 @@ const QueryResultPreview = ({ dispatch(sendRequest(item, collection.uid)); }; - switch (previewTab) { + switch (previewTab?.mode) { case 'preview-web': { const webViewSrc = data.replace('', ``); return ( diff --git a/packages/bruno-app/src/components/ResponsePane/QueryResult/index.js b/packages/bruno-app/src/components/ResponsePane/QueryResult/index.js index da713a2c..ccf08f31 100644 --- a/packages/bruno-app/src/components/ResponsePane/QueryResult/index.js +++ b/packages/bruno-app/src/components/ResponsePane/QueryResult/index.js @@ -12,6 +12,7 @@ import { useState } from 'react'; import { useMemo } from 'react'; import { useEffect } from 'react'; import { useTheme } from 'providers/Theme/index'; +import { uuid } from 'utils/common/index'; const formatResponse = (data, mode, filter) => { if (data === undefined) { @@ -66,18 +67,18 @@ const QueryResult = ({ item, collection, data, dataBuffer, width, disableRunEven const allowedPreviewModes = useMemo(() => { // Always show raw - const allowedPreviewModes = ['raw']; + const allowedPreviewModes = [{ mode: 'raw', name: 'Raw', uid: uuid() }]; if (mode.includes('html') && typeof data === 'string') { - allowedPreviewModes.unshift('preview-web'); + allowedPreviewModes.unshift({ mode: 'preview-web', name: 'Web', uid: uuid() }); } else if (mode.includes('image')) { - allowedPreviewModes.unshift('preview-image'); + allowedPreviewModes.unshift({ mode: 'preview-image', name: 'Image', uid: uuid() }); } else if (contentType.includes('pdf')) { - allowedPreviewModes.unshift('preview-pdf'); + allowedPreviewModes.unshift({ mode: 'preview-pdf', name: 'PDF', uid: uuid() }); } else if (contentType.includes('audio')) { - allowedPreviewModes.unshift('preview-audio'); + allowedPreviewModes.unshift({ mode: 'preview-audio', name: 'Audio', uid: uuid() }); } else if (contentType.includes('video')) { - allowedPreviewModes.unshift('preview-video'); + allowedPreviewModes.unshift({ mode: 'preview-video', name: 'Video', uid: uuid() }); } return allowedPreviewModes; @@ -86,7 +87,7 @@ const QueryResult = ({ item, collection, data, dataBuffer, width, disableRunEven const [previewTab, setPreviewTab] = useState(allowedPreviewModes[0]); // Ensure the active Tab is always allowed useEffect(() => { - if (!allowedPreviewModes.includes(previewTab)) { + if (!allowedPreviewModes.find((previewMode) => previewMode?.uid == previewTab?.uid)) { setPreviewTab(allowedPreviewModes[0]); } }, [previewTab, allowedPreviewModes]); @@ -98,12 +99,15 @@ const QueryResult = ({ item, collection, data, dataBuffer, width, disableRunEven return allowedPreviewModes.map((previewMode) => (
setPreviewTab(previewMode)} - key={previewMode} + key={previewMode?.uid} > - {previewMode.replace(/-(.*)/, ' ')} + {previewMode?.name}
)); }, [allowedPreviewModes, previewTab]); From 3cb3f8094f42bf1a743d290283c0feb372f0f522 Mon Sep 17 00:00:00 2001 From: lohit Date: Mon, 16 Sep 2024 17:13:19 +0530 Subject: [PATCH 31/59] chore: disableParsingResponseJson shim fn in safe mode (#3110) --- .../bruno-js/src/sandbox/quickjs/shims/bruno-request.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/bruno-js/src/sandbox/quickjs/shims/bruno-request.js b/packages/bruno-js/src/sandbox/quickjs/shims/bruno-request.js index 9be27fae..1edfaaad 100644 --- a/packages/bruno-js/src/sandbox/quickjs/shims/bruno-request.js +++ b/packages/bruno-js/src/sandbox/quickjs/shims/bruno-request.js @@ -105,6 +105,12 @@ const addBrunoRequestShimToContext = (vm, req) => { vm.setProp(reqObject, 'setTimeout', setTimeout); setTimeout.dispose(); + let disableParsingResponseJson = vm.newFunction('disableParsingResponseJson', function () { + req.disableParsingResponseJson(); + }); + vm.setProp(reqObject, 'disableParsingResponseJson', disableParsingResponseJson); + disableParsingResponseJson.dispose(); + vm.setProp(vm.global, 'req', reqObject); reqObject.dispose(); }; From 260996a0ceb7d99cbb802306057c9cda461db187 Mon Sep 17 00:00:00 2001 From: lohit Date: Tue, 17 Sep 2024 15:20:54 +0530 Subject: [PATCH 32/59] fix: revert enter key submit logic for new request form (#3114) --- .../src/components/Sidebar/NewRequest/index.js | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/packages/bruno-app/src/components/Sidebar/NewRequest/index.js b/packages/bruno-app/src/components/Sidebar/NewRequest/index.js index 50e7be27..549d162d 100644 --- a/packages/bruno-app/src/components/Sidebar/NewRequest/index.js +++ b/packages/bruno-app/src/components/Sidebar/NewRequest/index.js @@ -162,16 +162,7 @@ const NewRequest = ({ collection, item, isEphemeral, onClose }) => { return ( -
{ - if (e.key === 'Enter') { - e.preventDefault(); - formik.handleSubmit(); - } - }} - > +
-
v1.28.0
+
v1.29.0
diff --git a/packages/bruno-app/src/providers/App/useTelemetry.js b/packages/bruno-app/src/providers/App/useTelemetry.js index c82a342b..c4ff2c83 100644 --- a/packages/bruno-app/src/providers/App/useTelemetry.js +++ b/packages/bruno-app/src/providers/App/useTelemetry.js @@ -60,7 +60,7 @@ const trackStart = () => { event: 'start', properties: { os: platformLib.os.family, - version: '1.28.0' + version: '1.29.0' } }); }; diff --git a/packages/bruno-electron/package.json b/packages/bruno-electron/package.json index 2a0e9390..76eaf8a9 100644 --- a/packages/bruno-electron/package.json +++ b/packages/bruno-electron/package.json @@ -1,5 +1,5 @@ { - "version": "v1.28.0", + "version": "v1.29.0", "name": "bruno", "description": "Opensource API Client for Exploring and Testing APIs", "homepage": "https://www.usebruno.com", From 8b6e55dfc0e11beba4d972bdeb77fa6354375fd5 Mon Sep 17 00:00:00 2001 From: lohit Date: Wed, 18 Sep 2024 15:08:04 +0530 Subject: [PATCH 34/59] fix: axios transform request bind (#3123) --- packages/bruno-electron/src/ipc/network/axios-instance.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/bruno-electron/src/ipc/network/axios-instance.js b/packages/bruno-electron/src/ipc/network/axios-instance.js index f4ae59ce..d5208cea 100644 --- a/packages/bruno-electron/src/ipc/network/axios-instance.js +++ b/packages/bruno-electron/src/ipc/network/axios-instance.js @@ -61,7 +61,9 @@ function makeAxiosInstance() { return data; } - axios.defaults.transformRequest.forEach((tr) => data = tr(data, headers)); + axios.defaults.transformRequest.forEach(function (tr) { + data = tr.call(this, data, headers); + }, this); return data; }, proxy: false From 938e0560a20777bf3476ffb87072ec21594f079f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20D?= <72203064+TheoD02@users.noreply.github.com> Date: Wed, 18 Sep 2024 11:54:33 +0200 Subject: [PATCH 35/59] fix: handle case of text when invalid JSON (#3119) (#3120) * fix: handle case of text when invalid JSON (#3119) * don't stringify if json is invalid, and maintain indentation if stringified * stringify check --------- Co-authored-by: lohit --- .../src/components/ResponsePane/QueryResult/index.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/bruno-app/src/components/ResponsePane/QueryResult/index.js b/packages/bruno-app/src/components/ResponsePane/QueryResult/index.js index ccf08f31..8b233d51 100644 --- a/packages/bruno-app/src/components/ResponsePane/QueryResult/index.js +++ b/packages/bruno-app/src/components/ResponsePane/QueryResult/index.js @@ -19,16 +19,20 @@ const formatResponse = (data, mode, filter) => { return ''; } + if (data === null) { + return data; + } + if (mode.includes('json')) { let isValidJSON = false; - + try { isValidJSON = typeof JSON.parse(JSON.stringify(data)) === 'object'; } catch (error) { console.log('Error parsing JSON: ', error.message); } - if (!isValidJSON || data === null) { + if (!isValidJSON && typeof data === 'string') { return data; } From 07baa63e9d8548173cd9078483c059eaf279caa8 Mon Sep 17 00:00:00 2001 From: lohit Date: Wed, 18 Sep 2024 17:02:39 +0530 Subject: [PATCH 36/59] fix: validate docs links (#3122) * fix: validate docs links * fix: only allow external urls, ignore filesystem paths * fix: updates * chore: revert spacing --- packages/bruno-app/src/components/MarkDown/index.jsx | 3 ++- packages/bruno-electron/src/index.js | 11 +++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/bruno-app/src/components/MarkDown/index.jsx b/packages/bruno-app/src/components/MarkDown/index.jsx index 3c778c5a..e6582a8b 100644 --- a/packages/bruno-app/src/components/MarkDown/index.jsx +++ b/packages/bruno-app/src/components/MarkDown/index.jsx @@ -2,6 +2,7 @@ import MarkdownIt from 'markdown-it'; import * as MarkdownItReplaceLink from 'markdown-it-replace-link'; import StyledWrapper from './StyledWrapper'; import React from 'react'; +import { isValidUrl } from 'utils/url/index'; const Markdown = ({ collectionPath, onDoubleClick, content }) => { const markdownItOptions = { @@ -15,7 +16,7 @@ const Markdown = ({ collectionPath, onDoubleClick, content }) => { if (target.tagName === 'A') { event.preventDefault(); const href = target.getAttribute('href'); - if (href) { + if (href && isValidUrl(href)) { window.open(href, '_blank'); return; } diff --git a/packages/bruno-electron/src/index.js b/packages/bruno-electron/src/index.js index db5deeca..6efc531c 100644 --- a/packages/bruno-electron/src/index.js +++ b/packages/bruno-electron/src/index.js @@ -129,8 +129,15 @@ app.on('ready', async () => { } }); - mainWindow.webContents.setWindowOpenHandler((details) => { - require('electron').shell.openExternal(details.url); + mainWindow.webContents.setWindowOpenHandler(({ url }) => { + try { + const { protocol } = new URL(url); + if (['https:', 'http:'].includes(protocol)) { + require('electron').shell.openExternal(url); + } + } catch (e) { + console.error(e); + } return { action: 'deny' }; }); From 00fcd303489b8e9e18f5d69c38f2182d01aead34 Mon Sep 17 00:00:00 2001 From: Sanjai Kumar <84461672+sanjai0py@users.noreply.github.com> Date: Wed, 18 Sep 2024 18:07:55 +0530 Subject: [PATCH 37/59] 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 --- .../src/utils/exporters/postman-collection.js | 139 +++++++++++++----- .../exporters/postman-collection.spec.js | 81 ++++++++++ 2 files changed, 184 insertions(+), 36 deletions(-) create mode 100644 packages/bruno-app/src/utils/exporters/postman-collection.spec.js diff --git a/packages/bruno-app/src/utils/exporters/postman-collection.js b/packages/bruno-app/src/utils/exporters/postman-collection.js index 7260942f..9dabc0f6 100644 --- a/packages/bruno-app/src/utils/exporters/postman-collection.js +++ b/packages/bruno-app/src/utils/exporters/postman-collection.js @@ -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(/(? { + 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; 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 00000000..23c6690c --- /dev/null +++ b/packages/bruno-app/src/utils/exporters/postman-collection.spec.js @@ -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); + }); +}) From 89c8956523d0b78dffc019ccac0976224a53329c Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Thu, 19 Sep 2024 01:28:04 +0530 Subject: [PATCH 38/59] release: v1.29.1 --- packages/bruno-app/src/components/Sidebar/index.js | 2 +- packages/bruno-app/src/providers/App/useTelemetry.js | 2 +- packages/bruno-electron/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/bruno-app/src/components/Sidebar/index.js b/packages/bruno-app/src/components/Sidebar/index.js index a1eec2b3..15d3bdf2 100644 --- a/packages/bruno-app/src/components/Sidebar/index.js +++ b/packages/bruno-app/src/components/Sidebar/index.js @@ -132,7 +132,7 @@ const Sidebar = () => { Star */} -
v1.29.0
+
v1.29.1
diff --git a/packages/bruno-app/src/providers/App/useTelemetry.js b/packages/bruno-app/src/providers/App/useTelemetry.js index c4ff2c83..91eff8d5 100644 --- a/packages/bruno-app/src/providers/App/useTelemetry.js +++ b/packages/bruno-app/src/providers/App/useTelemetry.js @@ -60,7 +60,7 @@ const trackStart = () => { event: 'start', properties: { os: platformLib.os.family, - version: '1.29.0' + version: '1.29.1' } }); }; diff --git a/packages/bruno-electron/package.json b/packages/bruno-electron/package.json index 76eaf8a9..c89db720 100644 --- a/packages/bruno-electron/package.json +++ b/packages/bruno-electron/package.json @@ -1,5 +1,5 @@ { - "version": "v1.29.0", + "version": "v1.29.1", "name": "bruno", "description": "Opensource API Client for Exploring and Testing APIs", "homepage": "https://www.usebruno.com", From dd2b93e8cdb6737b2e4b4bf303397ed5be921b10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Linhart?= Date: Fri, 20 Sep 2024 10:39:11 +0200 Subject: [PATCH 39/59] fix: Allow to set custom user agent (#3146) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Linhart Lukáš --- packages/bruno-cli/src/utils/axios-instance.js | 7 +++++-- packages/bruno-electron/src/ipc/network/axios-instance.js | 8 +++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/bruno-cli/src/utils/axios-instance.js b/packages/bruno-cli/src/utils/axios-instance.js index af96806e..8f61066c 100644 --- a/packages/bruno-cli/src/utils/axios-instance.js +++ b/packages/bruno-cli/src/utils/axios-instance.js @@ -9,11 +9,14 @@ const { CLI_VERSION } = require('../constants'); */ function makeAxiosInstance() { /** @type {axios.AxiosInstance} */ - const instance = axios.create(); + const instance = axios.create({ + headers: { + "User-Agent": `bruno-runtime/${CLI_VERSION}` + } + }); instance.interceptors.request.use((config) => { config.headers['request-start-time'] = Date.now(); - config.headers['user-agent'] = `bruno-runtime/${CLI_VERSION}`; return config; }); diff --git a/packages/bruno-electron/src/ipc/network/axios-instance.js b/packages/bruno-electron/src/ipc/network/axios-instance.js index d5208cea..4f7f9f8f 100644 --- a/packages/bruno-electron/src/ipc/network/axios-instance.js +++ b/packages/bruno-electron/src/ipc/network/axios-instance.js @@ -7,6 +7,7 @@ const electronApp = require("electron"); const LOCAL_IPV6 = '::1'; const LOCAL_IPV4 = '127.0.0.1'; const LOCALHOST = 'localhost'; +const version = electronApp?.app?.getVersion()?.substring(1) ?? ""; const getTld = (hostname) => { if (!hostname) { @@ -66,9 +67,11 @@ function makeAxiosInstance() { }, this); return data; }, - proxy: false + proxy: false, + headers: { + "User-Agent": `bruno-runtime/${version}` + } }); - const version = electronApp?.app?.getVersion()?.substring(1) ?? ""; instance.interceptors.request.use(async (config) => { const url = URL.parse(config.url); @@ -88,7 +91,6 @@ function makeAxiosInstance() { } config.headers['request-start-time'] = Date.now(); - config.headers['user-agent'] = `bruno-runtime/${version}`; return config; }); From e019a96cd551f565388036652e6edf3f57510bbe Mon Sep 17 00:00:00 2001 From: Pragadesh-45 <54320162+Pragadesh-45@users.noreply.github.com> Date: Fri, 20 Sep 2024 14:12:19 +0530 Subject: [PATCH 40/59] feat: add logic to handle saving collection settings on shortcut (preview mode) (#3145) --- packages/bruno-app/src/providers/Hotkeys/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/bruno-app/src/providers/Hotkeys/index.js b/packages/bruno-app/src/providers/Hotkeys/index.js index 1b28b891..5b6bf1c0 100644 --- a/packages/bruno-app/src/providers/Hotkeys/index.js +++ b/packages/bruno-app/src/providers/Hotkeys/index.js @@ -7,7 +7,7 @@ import SaveRequest from 'components/RequestPane/SaveRequest'; import EnvironmentSettings from 'components/Environments/EnvironmentSettings'; import NetworkError from 'components/ResponsePane/NetworkError'; import NewRequest from 'components/Sidebar/NewRequest'; -import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions'; +import { sendRequest, saveRequest, saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions'; import { findCollectionByUid, findItemInCollection } from 'utils/collections'; import { closeTabs, switchTab } from 'providers/ReduxStore/slices/tabs'; @@ -54,6 +54,8 @@ export const HotkeysProvider = (props) => { const item = findItemInCollection(collection, activeTab.uid); if (item && item.uid) { dispatch(saveRequest(activeTab.uid, activeTab.collectionUid)); + } else if (activeTab.type === 'collection-settings') { + dispatch(saveCollectionRoot(collection.uid)); } else { // todo: when ephermal requests go live // setShowSaveRequestModal(true); From 563683b5c132e15c085992bf40d2434b281dd98e Mon Sep 17 00:00:00 2001 From: Pragadesh-45 <54320162+Pragadesh-45@users.noreply.github.com> Date: Fri, 20 Sep 2024 14:19:23 +0530 Subject: [PATCH 41/59] feature/useFocusTrap: Support focusable tab cycles in `Modal` (Update of PR #3075) (#3133) * enhance useFocusTrap: implemented focus trapping, hide non-focusable elements * add reference link --- .../bruno-app/src/hooks/useFocusTrap/index.js | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/packages/bruno-app/src/hooks/useFocusTrap/index.js b/packages/bruno-app/src/hooks/useFocusTrap/index.js index 18bead8b..760603d2 100644 --- a/packages/bruno-app/src/hooks/useFocusTrap/index.js +++ b/packages/bruno-app/src/hooks/useFocusTrap/index.js @@ -1,24 +1,28 @@ import { useEffect, useRef } from 'react'; const useFocusTrap = (modalRef) => { - const firstFocusableElementRef = useRef(null); - const lastFocusableElementRef = useRef(null); + // refer to this implementation for modal focus: https://stackoverflow.com/a/38865836 + const focusableSelector = 'a[href], area[href], input:not([disabled]):not([type="hidden"]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, *[tabindex]:not([tabindex="-1"]), *[contenteditable]'; + useEffect(() => { const modalElement = modalRef.current; if (!modalElement) return; - const focusableElements = modalElement.querySelectorAll( - 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])' - ); + const focusableElements = Array.from(document.querySelectorAll(focusableSelector)); + const modalFocusableElements = Array.from(modalElement.querySelectorAll(focusableSelector)); + const elementsToHide = focusableElements.filter(el => !modalFocusableElements.includes(el)); - if (focusableElements.length === 0) return; + // Hide elements outside the modal + elementsToHide.forEach(el => { + const originalTabIndex = el.getAttribute('tabindex'); + el.setAttribute('data-tabindex', originalTabIndex || 'inline'); + el.setAttribute('tabindex', -1); + }); - const firstElement = focusableElements[0]; - const lastElement = focusableElements[focusableElements.length - 1]; - - firstFocusableElementRef.current = firstElement; - lastFocusableElementRef.current = lastElement; + // Set focus to the first focusable element in the modal + const firstElement = modalFocusableElements[0]; + const lastElement = modalFocusableElements[modalFocusableElements.length - 1]; const handleKeyDown = (event) => { if (event.key === 'Tab') { @@ -36,6 +40,12 @@ const useFocusTrap = (modalRef) => { return () => { modalElement.removeEventListener('keydown', handleKeyDown); + + // Restore original tabindex values + elementsToHide.forEach(el => { + const originalTabIndex = el.getAttribute('data-tabindex'); + el.setAttribute('tabindex', originalTabIndex === 'inline' ? '' : originalTabIndex); + }); }; }, [modalRef]); }; From b60c799645b94748d570d77fe99049bd483341b3 Mon Sep 17 00:00:00 2001 From: Pragadesh-45 <54320162+Pragadesh-45@users.noreply.github.com> Date: Fri, 20 Sep 2024 14:22:12 +0530 Subject: [PATCH 42/59] fix: accessibility issue in side bar's footer (#3130) * fix: accessibility issues in side bar footer icons * small accessibility changes & formatting * chore: fixed misspell * chore: code cleanup * added proper aria-labels and added with the footer as `ul` * chore: code cleanup --------- Co-authored-by: Shrilakshmi Shastry --- .../src/components/Notifications/index.js | 19 +-- .../src/components/Sidebar/TitleBar/index.js | 14 +-- .../bruno-app/src/components/Sidebar/index.js | 114 +++++++++++++----- 3 files changed, 101 insertions(+), 46 deletions(-) diff --git a/packages/bruno-app/src/components/Notifications/index.js b/packages/bruno-app/src/components/Notifications/index.js index 24553854..ba257bf4 100644 --- a/packages/bruno-app/src/components/Notifications/index.js +++ b/packages/bruno-app/src/components/Notifications/index.js @@ -93,10 +93,12 @@ const Notifications = () => { dispatch(fetchNotifications()); setShowNotificationsModal(true); }} + aria-label="Check all Notifications" > - + 0 ? 'bell' : ''}`} /> @@ -133,8 +135,9 @@ const Notifications = () => { {notifications?.slice(notificationsStartIndex, notificationsEndIndex)?.map((notification) => (
  • {notification?.title}
    @@ -144,8 +147,9 @@ const Notifications = () => {
    } placement="bottom-start">
    { const leftSidebarWidth = useSelector((state) => state.app.leftSidebarWidth); const preferencesOpen = useSelector((state) => state.app.showPreferences); - const [goldenEditonOpen, setGoldenEditonOpen] = useState(false); + const [goldenEditionOpen, setGoldenEditionOpen] = useState(false); const [asideWidth, setAsideWidth] = useState(leftSidebarWidth); const [cookiesOpen, setCookiesOpen] = useState(false); @@ -83,10 +83,43 @@ const Sidebar = () => { return ( +
    - + ); }; From 637e53421eba04a9d9ef4caf31505a7777225f12 Mon Sep 17 00:00:00 2001 From: Sanjai Kumar <84461672+sanjai0py@users.noreply.github.com> Date: Fri, 20 Sep 2024 17:28:53 +0530 Subject: [PATCH 43/59] Feat/api key auth (#2478) * feat: Added ApiKeyAuth component * feat: Add support for API Key authentication - Added the ApiKeyAuth component to handle API Key authentication mode. - Updated the AuthMode component to include an option for API Key authentication. - Updated the collections schema to include validation for API Key authentication. - Updated the collectionsSlice to handle API Key authentication in the Redux store. * refactor: input value handlers - Removed the separate handleKeyChange, handlePlacementChange and handleValueChange functions and consolidated them into handleAuthChange. * feat: Update prepare-request to handle API Key authentication in query parameters * refactor: handling the queryparams placement api key values in the ConfigureRequest function * refactor: added collection level api key auth * refactor: updated collection export function * refactor: add default placement for API key authentication in ApiKeyAuth component * refactor: add default placement for API key authentication in ApiKeyAuth component in CollectionSettings * refactor: update generateAuth function to handle API key authentication in postman collection exporter * refactor: fix typo in API key placement for collection export * Made minor changes in the logic. * Updated the importers for postman to handle new auth type. --- .../Auth/ApiKeyAuth/StyledWrapper.js | 56 +++++++++ .../Auth/ApiKeyAuth/index.js | 109 +++++++++++++++++ .../CollectionSettings/Auth/AuthMode/index.js | 9 ++ .../CollectionSettings/Auth/index.js | 4 + .../Auth/ApiKeyAuth/StyledWrapper.js | 57 +++++++++ .../RequestPane/Auth/ApiKeyAuth/index.js | 114 ++++++++++++++++++ .../RequestPane/Auth/AuthMode/index.js | 9 ++ .../src/components/RequestPane/Auth/index.js | 4 + .../ReduxStore/slices/collections/index.js | 7 ++ .../bruno-app/src/utils/collections/index.js | 28 +++++ .../src/utils/exporters/postman-collection.js | 23 +++- .../src/utils/importers/postman-collection.js | 8 +- .../bruno-electron/src/ipc/network/index.js | 15 +++ .../src/ipc/network/prepare-request.js | 18 +++ packages/bruno-lang/v2/src/bruToJson.js | 25 +++- .../bruno-lang/v2/src/collectionBruToJson.js | 25 +++- packages/bruno-lang/v2/src/jsonToBru.js | 10 ++ .../bruno-lang/v2/src/jsonToCollectionBru.js | 9 ++ .../bruno-schema/src/collections/index.js | 11 +- 19 files changed, 535 insertions(+), 6 deletions(-) create mode 100644 packages/bruno-app/src/components/CollectionSettings/Auth/ApiKeyAuth/StyledWrapper.js create mode 100644 packages/bruno-app/src/components/CollectionSettings/Auth/ApiKeyAuth/index.js create mode 100644 packages/bruno-app/src/components/RequestPane/Auth/ApiKeyAuth/StyledWrapper.js create mode 100644 packages/bruno-app/src/components/RequestPane/Auth/ApiKeyAuth/index.js diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/ApiKeyAuth/StyledWrapper.js b/packages/bruno-app/src/components/CollectionSettings/Auth/ApiKeyAuth/StyledWrapper.js new file mode 100644 index 00000000..aaa30d7f --- /dev/null +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/ApiKeyAuth/StyledWrapper.js @@ -0,0 +1,56 @@ +import styled from 'styled-components'; + +const Wrapper = styled.div` + label { + font-size: 0.8125rem; + } + + .single-line-editor-wrapper { + padding: 0.15rem 0.4rem; + border-radius: 3px; + border: solid 1px ${(props) => props.theme.input.border}; + background-color: ${(props) => props.theme.input.bg}; + } + + .auth-placement-selector { + padding: 0.5rem 0px; + border-radius: 3px; + border: solid 1px ${(props) => props.theme.input.border}; + background-color: ${(props) => props.theme.input.bg}; + + .dropdown { + width: fit-content; + + div[data-tippy-root] { + width: fit-content; + } + .tippy-box { + width: fit-content; + max-width: none !important; + + .tippy-content: { + width: fit-content; + max-width: none !important; + } + } + } + + .auth-type-label { + width: fit-content; + color: ${(props) => props.theme.colors.text.yellow}; + justify-content: space-between; + padding: 0 0.5rem; + } + + .dropdown-item { + padding: 0.2rem 0.6rem !important; + } + } + + .caret { + color: rgb(140, 140, 140); + fill: rgb(140 140 140); + } +`; + +export default Wrapper; diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/ApiKeyAuth/index.js b/packages/bruno-app/src/components/CollectionSettings/Auth/ApiKeyAuth/index.js new file mode 100644 index 00000000..74d348c6 --- /dev/null +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/ApiKeyAuth/index.js @@ -0,0 +1,109 @@ +import React, { useRef, forwardRef, useEffect } from 'react'; +import { useDispatch } from 'react-redux'; +import get from 'lodash/get'; +import { IconCaretDown } from '@tabler/icons'; +import Dropdown from 'components/Dropdown'; +import { useTheme } from 'providers/Theme'; +import SingleLineEditor from 'components/SingleLineEditor'; +import { updateCollectionAuth } from 'providers/ReduxStore/slices/collections'; +import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions'; +import StyledWrapper from './StyledWrapper'; +import { humanizeRequestAPIKeyPlacement } from 'utils/collections'; + +const ApiKeyAuth = ({ collection }) => { + const dispatch = useDispatch(); + const { storedTheme } = useTheme(); + const dropdownTippyRef = useRef(); + const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref); + + const apikeyAuth = get(collection, 'root.request.auth.apikey', {}); + + const handleSave = () => dispatch(saveCollectionRoot(collection.uid)); + + const Icon = forwardRef((props, ref) => { + return ( +
    + {humanizeRequestAPIKeyPlacement(apikeyAuth?.placement)} + +
    + ); + }); + + const handleAuthChange = (property, value) => { + dispatch( + updateCollectionAuth({ + mode: 'apikey', + collectionUid: collection.uid, + content: { + ...apikeyAuth, + [property]: value + } + }) + ); + }; + + useEffect(() => { + !apikeyAuth?.placement && + dispatch( + updateCollectionAuth({ + mode: 'apikey', + collectionUid: collection.uid, + content: { + placement: 'header' + } + }) + ); + }, [apikeyAuth]); + + return ( + + +
    + handleAuthChange('key', val)} + collection={collection} + /> +
    + + +
    + handleAuthChange('value', val)} + collection={collection} + /> +
    + + +
    + } placement="bottom-end"> +
    { + dropdownTippyRef.current.hide(); + handleAuthChange('placement', 'header'); + }} + > + Header +
    +
    { + dropdownTippyRef.current.hide(); + handleAuthChange('placement', 'queryparams'); + }} + > + Query Params +
    +
    +
    +
    + ); +}; + +export default ApiKeyAuth; diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/AuthMode/index.js b/packages/bruno-app/src/components/CollectionSettings/Auth/AuthMode/index.js index 7280e672..c8e208ac 100644 --- a/packages/bruno-app/src/components/CollectionSettings/Auth/AuthMode/index.js +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/AuthMode/index.js @@ -79,6 +79,15 @@ const AuthMode = ({ collection }) => { > Oauth2
    +
    { + dropdownTippyRef.current.hide(); + onModeChange('apikey'); + }} + > + API Key +
    { diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/index.js b/packages/bruno-app/src/components/CollectionSettings/Auth/index.js index 38a958e9..85673782 100644 --- a/packages/bruno-app/src/components/CollectionSettings/Auth/index.js +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/index.js @@ -6,6 +6,7 @@ import AwsV4Auth from './AwsV4Auth'; import BearerAuth from './BearerAuth'; import BasicAuth from './BasicAuth'; import DigestAuth from './DigestAuth'; +import ApiKeyAuth from './ApiKeyAuth/'; import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions'; import StyledWrapper from './StyledWrapper'; import OAuth2 from './OAuth2'; @@ -33,6 +34,9 @@ const Auth = ({ collection }) => { case 'oauth2': { return ; } + case 'apikey': { + return ; + } } }; diff --git a/packages/bruno-app/src/components/RequestPane/Auth/ApiKeyAuth/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/Auth/ApiKeyAuth/StyledWrapper.js new file mode 100644 index 00000000..3ef007f8 --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/Auth/ApiKeyAuth/StyledWrapper.js @@ -0,0 +1,57 @@ +import styled from 'styled-components'; + +const Wrapper = styled.div` + label { + font-size: 0.8125rem; + } + + .single-line-editor-wrapper { + max-width: 400px; + padding: 0.15rem 0.4rem; + border-radius: 3px; + border: solid 1px ${(props) => props.theme.input.border}; + background-color: ${(props) => props.theme.input.bg}; + } + + .auth-placement-selector { + padding: 0.5rem 0px; + border-radius: 3px; + border: solid 1px ${(props) => props.theme.input.border}; + background-color: ${(props) => props.theme.input.bg}; + + .dropdown { + width: fit-content; + + div[data-tippy-root] { + width: fit-content; + } + .tippy-box { + width: fit-content; + max-width: none !important; + + .tippy-content: { + width: fit-content; + max-width: none !important; + } + } + } + + .auth-type-label { + width: fit-content; + color: ${(props) => props.theme.colors.text.yellow}; + justify-content: space-between; + padding: 0 0.5rem; + } + + .dropdown-item { + padding: 0.2rem 0.6rem !important; + } + } + + .caret { + color: rgb(140, 140, 140); + fill: rgb(140 140 140); + } +`; + +export default Wrapper; diff --git a/packages/bruno-app/src/components/RequestPane/Auth/ApiKeyAuth/index.js b/packages/bruno-app/src/components/RequestPane/Auth/ApiKeyAuth/index.js new file mode 100644 index 00000000..22a16563 --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/Auth/ApiKeyAuth/index.js @@ -0,0 +1,114 @@ +import React, { useRef, forwardRef, useEffect } from 'react'; +import { useDispatch } from 'react-redux'; +import get from 'lodash/get'; +import { IconCaretDown } from '@tabler/icons'; +import Dropdown from 'components/Dropdown'; +import { useTheme } from 'providers/Theme'; +import SingleLineEditor from 'components/SingleLineEditor'; +import { updateAuth } from 'providers/ReduxStore/slices/collections'; +import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions'; +import StyledWrapper from './StyledWrapper'; +import { humanizeRequestAPIKeyPlacement } from 'utils/collections'; + +const ApiKeyAuth = ({ item, collection }) => { + const dispatch = useDispatch(); + const { storedTheme } = useTheme(); + const dropdownTippyRef = useRef(); + const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref); + + const apikeyAuth = item.draft ? get(item, 'draft.request.auth.apikey', {}) : get(item, 'request.auth.apikey', {}); + + const handleRun = () => dispatch(sendRequest(item, collection.uid)); + const handleSave = () => dispatch(saveRequest(item.uid, collection.uid)); + + const Icon = forwardRef((props, ref) => { + return ( +
    + {humanizeRequestAPIKeyPlacement(apikeyAuth?.placement)} + +
    + ); + }); + + const handleAuthChange = (property, value) => { + dispatch( + updateAuth({ + mode: 'apikey', + collectionUid: collection.uid, + itemUid: item.uid, + content: { + ...apikeyAuth, + [property]: value + } + }) + ); + }; + + useEffect(() => { + !apikeyAuth?.placement && + dispatch( + updateAuth({ + mode: 'apikey', + collectionUid: collection.uid, + itemUid: item.uid, + content: { + placement: 'header' + } + }) + ); + }, [apikeyAuth]); + + return ( + + +
    + handleAuthChange('key', val)} + onRun={handleRun} + collection={collection} + /> +
    + + +
    + handleAuthChange('value', val)} + onRun={handleRun} + collection={collection} + /> +
    + + +
    + } placement="bottom-end"> +
    { + dropdownTippyRef.current.hide(); + handleAuthChange('placement', 'header'); + }} + > + Header +
    +
    { + dropdownTippyRef.current.hide(); + handleAuthChange('placement', 'queryparams'); + }} + > + Query Params +
    +
    +
    +
    + ); +}; + +export default ApiKeyAuth; diff --git a/packages/bruno-app/src/components/RequestPane/Auth/AuthMode/index.js b/packages/bruno-app/src/components/RequestPane/Auth/AuthMode/index.js index 2367d964..9de35e5f 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/AuthMode/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/AuthMode/index.js @@ -80,6 +80,15 @@ const AuthMode = ({ item, collection }) => { > OAuth 2.0
    +
    { + dropdownTippyRef?.current?.hide(); + onModeChange('apikey'); + }} + > + API Key +
    { diff --git a/packages/bruno-app/src/components/RequestPane/Auth/index.js b/packages/bruno-app/src/components/RequestPane/Auth/index.js index 51a1450f..2786f6d6 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/index.js @@ -5,6 +5,7 @@ import AwsV4Auth from './AwsV4Auth'; import BearerAuth from './BearerAuth'; import BasicAuth from './BasicAuth'; import DigestAuth from './DigestAuth'; +import ApiKeyAuth from './ApiKeyAuth'; import StyledWrapper from './StyledWrapper'; import { humanizeRequestAuthMode } from 'utils/collections/index'; import OAuth2 from './OAuth2/index'; @@ -32,6 +33,9 @@ const Auth = ({ item, collection }) => { case 'oauth2': { return ; } + case 'apikey': { + return ; + } case 'inherit': { return (
    diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js index e8fb4d60..34a6c6af 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js @@ -477,6 +477,10 @@ export const collectionsSlice = createSlice({ item.draft.request.auth.mode = 'oauth2'; item.draft.request.auth.oauth2 = action.payload.content; break; + case 'apikey': + item.draft.request.auth.mode = 'apikey'; + item.draft.request.auth.apikey = action.payload.content; + break; } } } @@ -1137,6 +1141,9 @@ export const collectionsSlice = createSlice({ case 'oauth2': set(collection, 'root.request.auth.oauth2', action.payload.content); break; + case 'apikey': + set(collection, 'root.request.auth.apikey', action.payload.content); + break; } } }, diff --git a/packages/bruno-app/src/utils/collections/index.js b/packages/bruno-app/src/utils/collections/index.js index d872a668..99d9b269 100644 --- a/packages/bruno-app/src/utils/collections/index.js +++ b/packages/bruno-app/src/utils/collections/index.js @@ -372,6 +372,14 @@ export const transformCollectionToSaveToExportAsFile = (collection, options = {} break; } break; + case 'apikey': + di.request.auth.apikey = { + key: get(si.request, 'auth.apikey.key', ''), + value: get(si.request, 'auth.apikey.value', ''), + placement: get(si.request, 'auth.apikey.placement', 'header') + }; + break; + default: break; } @@ -661,6 +669,26 @@ export const humanizeRequestAuthMode = (mode) => { label = 'OAuth 2.0'; break; } + case 'apikey': { + label = 'API Key'; + break; + } + } + + return label; +}; + +export const humanizeRequestAPIKeyPlacement = (placement) => { + let label = 'Header'; + switch (placement) { + case 'header': { + label = 'Header'; + break; + } + case 'queryparams': { + label = 'Query Params'; + break; + } } return label; diff --git a/packages/bruno-app/src/utils/exporters/postman-collection.js b/packages/bruno-app/src/utils/exporters/postman-collection.js index 9dabc0f6..9511ffce 100644 --- a/packages/bruno-app/src/utils/exporters/postman-collection.js +++ b/packages/bruno-app/src/utils/exporters/postman-collection.js @@ -246,7 +246,7 @@ export const exportCollection = (collection) => { }; const generateAuth = (itemAuth) => { - switch (itemAuth) { + switch (itemAuth?.mode) { case 'bearer': return { type: 'bearer', @@ -273,6 +273,27 @@ export const exportCollection = (collection) => { ] }; } + case 'apikey': { + return { + type: 'apikey', + apikey: [ + { + key: 'key', + value: itemAuth.apikey.key, + type: 'string' + }, + { + key: 'value', + value: itemAuth.apikey.value, + type: 'string' + } + ] + }; + } + default: { + console.error('Unsupported auth mode:', itemAuth.mode); + return null; + } } }; diff --git a/packages/bruno-app/src/utils/importers/postman-collection.js b/packages/bruno-app/src/utils/importers/postman-collection.js index e9eaccf8..51575611 100644 --- a/packages/bruno-app/src/utils/importers/postman-collection.js +++ b/packages/bruno-app/src/utils/importers/postman-collection.js @@ -1,4 +1,3 @@ -import each from 'lodash/each'; import get from 'lodash/get'; import fileDialog from 'file-dialog'; import { uuid } from 'utils/common'; @@ -292,6 +291,13 @@ const importPostmanV2CollectionItem = (brunoParent, item, parentAuth, options) = region: authValues.region, profileName: '' }; + } else if (auth.type === 'apikey'){ + brunoRequestItem.request.auth.mode = 'apikey'; + brunoRequestItem.request.auth.apikey = { + key: authValues.key, + value: authValues.value, + placement: "header" //By default we are placing the apikey values in headers! + } } } diff --git a/packages/bruno-electron/src/ipc/network/index.js b/packages/bruno-electron/src/ipc/network/index.js index 768505e2..d7ed96d2 100644 --- a/packages/bruno-electron/src/ipc/network/index.js +++ b/packages/bruno-electron/src/ipc/network/index.js @@ -317,9 +317,24 @@ const configureRequest = async ( } } + // Add API key to the URL + if (request.apiKeyAuthValueForQueryParams && request.apiKeyAuthValueForQueryParams.placement === 'queryparams') { + const urlObj = new URL(request.url); + + // Interpolate key and value as they can be variables before adding to the URL. + const key = interpolateString(request.apiKeyAuthValueForQueryParams.key, interpolationOptions); + const value = interpolateString(request.apiKeyAuthValueForQueryParams.value, interpolationOptions); + + urlObj.searchParams.set(key, value); + request.url = urlObj.toString(); + } + // Remove pathParams, already in URL (Issue #2439) delete request.pathParams; + // Remove apiKeyAuthValueForQueryParams, already interpolated and added to URL + delete request.apiKeyAuthValueForQueryParams; + return axiosInstance; }; diff --git a/packages/bruno-electron/src/ipc/network/prepare-request.js b/packages/bruno-electron/src/ipc/network/prepare-request.js index 6601347b..75b0f2c0 100644 --- a/packages/bruno-electron/src/ipc/network/prepare-request.js +++ b/packages/bruno-electron/src/ipc/network/prepare-request.js @@ -219,6 +219,15 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => { password: get(collectionAuth, 'digest.password') }; break; + case 'apikey': + const apiKeyAuth = get(collectionAuth, 'apikey'); + if (apiKeyAuth.placement === 'header') { + axiosRequest.headers[apiKeyAuth.key] = apiKeyAuth.value; + } else if (apiKeyAuth.placement === 'queryparams') { + // If the API key authentication is set and its placement is 'queryparams', add it to the axios request object. This will be used in the configureRequest function to append the API key to the query parameters of the request URL. + axiosRequest.apiKeyAuthValueForQueryParams = apiKeyAuth; + } + break; } } @@ -287,6 +296,15 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => { break; } break; + case 'apikey': + const apiKeyAuth = get(request, 'auth.apikey'); + if (apiKeyAuth.placement === 'header') { + axiosRequest.headers[apiKeyAuth.key] = apiKeyAuth.value; + } else if (apiKeyAuth.placement === 'queryparams') { + // If the API key authentication is set and its placement is 'queryparams', add it to the axios request object. This will be used in the configureRequest function to append the API key to the query parameters of the request URL. + axiosRequest.apiKeyAuthValueForQueryParams = apiKeyAuth; + } + break; } } diff --git a/packages/bruno-lang/v2/src/bruToJson.js b/packages/bruno-lang/v2/src/bruToJson.js index 08e4332c..84890c92 100644 --- a/packages/bruno-lang/v2/src/bruToJson.js +++ b/packages/bruno-lang/v2/src/bruToJson.js @@ -23,7 +23,7 @@ const { outdentString } = require('../../v1/src/utils'); */ const grammar = ohm.grammar(`Bru { BruFile = (meta | http | query | params | headers | auths | bodies | varsandassert | script | tests | docs)* - auths = authawsv4 | authbasic | authbearer | authdigest | authOAuth2 + auths = authawsv4 | authbasic | authbearer | authdigest | authOAuth2 | authapikey bodies = bodyjson | bodytext | bodyxml | bodysparql | bodygraphql | bodygraphqlvars | bodyforms | body bodyforms = bodyformurlencoded | bodymultipart params = paramspath | paramsquery @@ -88,6 +88,7 @@ const grammar = ohm.grammar(`Bru { authbearer = "auth:bearer" dictionary authdigest = "auth:digest" dictionary authOAuth2 = "auth:oauth2" dictionary + authapikey = "auth:apikey" dictionary body = "body" st* "{" nl* textblock tagend bodyjson = "body:json" st* "{" nl* textblock tagend @@ -483,6 +484,28 @@ const sem = grammar.createSemantics().addAttribute('ast', { } }; }, + authapikey(_1, dictionary) { + const auth = mapPairListToKeyValPairs(dictionary.ast, false); + + const findValueByName = (name) => { + const item = _.find(auth, { name }); + return item ? item.value : ''; + }; + + const key = findValueByName('key'); + const value = findValueByName('value'); + const placement = findValueByName('placement'); + + return { + auth: { + apikey: { + key, + value, + placement + } + } + }; + }, bodyformurlencoded(_1, dictionary) { return { body: { diff --git a/packages/bruno-lang/v2/src/collectionBruToJson.js b/packages/bruno-lang/v2/src/collectionBruToJson.js index 3c02a622..513c4102 100644 --- a/packages/bruno-lang/v2/src/collectionBruToJson.js +++ b/packages/bruno-lang/v2/src/collectionBruToJson.js @@ -4,7 +4,7 @@ const { outdentString } = require('../../v1/src/utils'); const grammar = ohm.grammar(`Bru { BruFile = (meta | query | headers | auth | auths | vars | script | tests | docs)* - auths = authawsv4 | authbasic | authbearer | authdigest | authOAuth2 + auths = authawsv4 | authbasic | authbearer | authdigest | authOAuth2 | authapikey nl = "\\r"? "\\n" st = " " | "\\t" @@ -43,6 +43,7 @@ const grammar = ohm.grammar(`Bru { authbearer = "auth:bearer" dictionary authdigest = "auth:digest" dictionary authOAuth2 = "auth:oauth2" dictionary + authapikey = "auth:apikey" dictionary script = scriptreq | scriptres scriptreq = "script:pre-request" st* "{" nl* textblock tagend @@ -293,6 +294,28 @@ const sem = grammar.createSemantics().addAttribute('ast', { } }; }, + authapikey(_1, dictionary) { + const auth = mapPairListToKeyValPairs(dictionary.ast, false); + + const findValueByName = (name) => { + const item = _.find(auth, { name }); + return item ? item.value : ''; + }; + + const key = findValueByName('key'); + const value = findValueByName('value'); + const placement = findValueByName('placement'); + + return { + auth: { + apikey: { + key, + value, + placement + } + } + }; + }, varsreq(_1, dictionary) { const vars = mapPairListToKeyValPairs(dictionary.ast); _.each(vars, (v) => { diff --git a/packages/bruno-lang/v2/src/jsonToBru.js b/packages/bruno-lang/v2/src/jsonToBru.js index dd3be594..30bec13e 100644 --- a/packages/bruno-lang/v2/src/jsonToBru.js +++ b/packages/bruno-lang/v2/src/jsonToBru.js @@ -200,6 +200,16 @@ ${indentString(`scope: ${auth?.oauth2?.scope || ''}`)} } } + if (auth && auth.apikey) { + bru += `auth:apikey { +${indentString(`key: ${auth?.apikey?.key || ''}`)} +${indentString(`value: ${auth?.apikey?.value || ''}`)} +${indentString(`placement: ${auth?.apikey?.placement || ''}`)} +} + +`; + } + if (body && body.json && body.json.length) { bru += `body:json { ${indentString(body.json)} diff --git a/packages/bruno-lang/v2/src/jsonToCollectionBru.js b/packages/bruno-lang/v2/src/jsonToCollectionBru.js index 11df88da..6462efb3 100644 --- a/packages/bruno-lang/v2/src/jsonToCollectionBru.js +++ b/packages/bruno-lang/v2/src/jsonToCollectionBru.js @@ -111,6 +111,15 @@ ${indentString(`username: ${auth.digest.username}`)} ${indentString(`password: ${auth.digest.password}`)} } +`; + } + + if (auth && auth.apikey) { + bru += `auth:apikey { +${indentString(`key: ${auth?.apikey?.key || ''}`)} +${indentString(`value: ${auth?.apikey?.value || ''}`)} +${indentString(`placement: ${auth?.apikey?.placement || ''}`)} +} `; } diff --git a/packages/bruno-schema/src/collections/index.js b/packages/bruno-schema/src/collections/index.js index eeb4e83d..2934a60d 100644 --- a/packages/bruno-schema/src/collections/index.js +++ b/packages/bruno-schema/src/collections/index.js @@ -177,15 +177,22 @@ const oauth2Schema = Yup.object({ .noUnknown(true) .strict(); +const authApiKeySchema = Yup.object({ + key: Yup.string().nullable(), + value: Yup.string().nullable(), + placement: Yup.string().oneOf(['header', 'queryparams']).nullable() +}); + const authSchema = Yup.object({ mode: Yup.string() - .oneOf(['inherit', 'none', 'awsv4', 'basic', 'bearer', 'digest', 'oauth2']) + .oneOf(['inherit', 'none', 'awsv4', 'basic', 'bearer', 'digest', 'oauth2', 'apikey']) .required('mode is required'), awsv4: authAwsV4Schema.nullable(), basic: authBasicSchema.nullable(), bearer: authBearerSchema.nullable(), digest: authDigestSchema.nullable(), - oauth2: oauth2Schema.nullable() + oauth2: oauth2Schema.nullable(), + apikey: authApiKeySchema.nullable() }) .noUnknown(true) .strict() From d7ff4e7ee0d38ab9a0a6fbb4be50fc4df15319b0 Mon Sep 17 00:00:00 2001 From: Sitaram Rathi <57089237+srrathi@users.noreply.github.com> Date: Sat, 21 Sep 2024 09:31:38 +0530 Subject: [PATCH 44/59] added generate code button in query url bar (#3099) --- .../components/RequestPane/QueryUrl/index.js | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js b/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js index 15334d2f..f6d4ff30 100644 --- a/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js +++ b/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js @@ -5,10 +5,12 @@ import { requestUrlChanged, updateRequestMethod } from 'providers/ReduxStore/sli import { saveRequest } from 'providers/ReduxStore/slices/collections/actions'; import HttpMethodSelector from './HttpMethodSelector'; import { useTheme } from 'providers/Theme'; -import { IconDeviceFloppy, IconArrowRight } from '@tabler/icons'; +import { IconDeviceFloppy, IconArrowRight, IconCode } from '@tabler/icons'; import SingleLineEditor from 'components/SingleLineEditor'; import { isMacOS } from 'utils/common/platform'; import StyledWrapper from './StyledWrapper'; +import GenerateCodeItem from 'components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/index'; +import toast from 'react-hot-toast'; const QueryUrl = ({ item, collection, handleRun }) => { const { theme, storedTheme } = useTheme(); @@ -20,6 +22,7 @@ const QueryUrl = ({ item, collection, handleRun }) => { const editorRef = useRef(null); const [methodSelectorWidth, setMethodSelectorWidth] = useState(90); + const [generateCodeItemModalOpen, setGenerateCodeItemModalOpen] = useState(false); useEffect(() => { const el = document.querySelector('.method-selector-container'); @@ -65,6 +68,15 @@ const QueryUrl = ({ item, collection, handleRun }) => { ); }; + const handleGenerateCode = (e) => { + e.stopPropagation(); + if (item.request.url !== '' || (item.draft?.request.url !== undefined && item.draft?.request.url !== '')) { + setGenerateCodeItemModalOpen(true); + } else { + toast.error('URL is required'); + } + }; + return (
    @@ -90,6 +102,22 @@ const QueryUrl = ({ item, collection, handleRun }) => { item={item} />
    +
    { + handleGenerateCode(e); + }} + > + + + Generate Code + +
    { @@ -111,6 +139,9 @@ const QueryUrl = ({ item, collection, handleRun }) => {
    + {generateCodeItemModalOpen && ( + setGenerateCodeItemModalOpen(false)} /> + )} ); }; From f5a452516144fc58fc8efebc04bf75c300b0219d Mon Sep 17 00:00:00 2001 From: lohit Date: Sat, 21 Sep 2024 11:26:38 +0530 Subject: [PATCH 45/59] fix: stringify response data is not a string (#3155) --- .../src/components/ResponsePane/QueryResult/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/bruno-app/src/components/ResponsePane/QueryResult/index.js b/packages/bruno-app/src/components/ResponsePane/QueryResult/index.js index 8b233d51..e9acd1f5 100644 --- a/packages/bruno-app/src/components/ResponsePane/QueryResult/index.js +++ b/packages/bruno-app/src/components/ResponsePane/QueryResult/index.js @@ -55,7 +55,11 @@ const formatResponse = (data, mode, filter) => { return safeStringifyJSON(parsed, true); } - return data; + if (typeof data === 'string') { + return data; + } + + return safeStringifyJSON(data, true); }; const QueryResult = ({ item, collection, data, dataBuffer, width, disableRunEventListener, headers, error }) => { From bad1302cb5a330f932e2bb9db661a8632f5fd912 Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Sat, 21 Sep 2024 13:43:34 +0530 Subject: [PATCH 46/59] release: v1.30.0 --- packages/bruno-app/src/components/Sidebar/index.js | 2 +- packages/bruno-app/src/providers/App/useTelemetry.js | 2 +- packages/bruno-electron/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/bruno-app/src/components/Sidebar/index.js b/packages/bruno-app/src/components/Sidebar/index.js index 6fcded69..fd878521 100644 --- a/packages/bruno-app/src/components/Sidebar/index.js +++ b/packages/bruno-app/src/components/Sidebar/index.js @@ -185,7 +185,7 @@ const Sidebar = () => { Star */}
    -
    v1.29.1
    +
    v1.30.0
    diff --git a/packages/bruno-app/src/providers/App/useTelemetry.js b/packages/bruno-app/src/providers/App/useTelemetry.js index 91eff8d5..cd69d08c 100644 --- a/packages/bruno-app/src/providers/App/useTelemetry.js +++ b/packages/bruno-app/src/providers/App/useTelemetry.js @@ -60,7 +60,7 @@ const trackStart = () => { event: 'start', properties: { os: platformLib.os.family, - version: '1.29.1' + version: '1.30.0' } }); }; diff --git a/packages/bruno-electron/package.json b/packages/bruno-electron/package.json index c89db720..ad1b2c53 100644 --- a/packages/bruno-electron/package.json +++ b/packages/bruno-electron/package.json @@ -1,5 +1,5 @@ { - "version": "v1.29.1", + "version": "v1.30.0", "name": "bruno", "description": "Opensource API Client for Exploring and Testing APIs", "homepage": "https://www.usebruno.com", From 6c6693757e462cc10cb80963c3c2c6e8febe4823 Mon Sep 17 00:00:00 2001 From: lohit Date: Sat, 21 Sep 2024 17:56:28 +0530 Subject: [PATCH 47/59] fix: add missing import (#3157) --- packages/bruno-app/src/utils/importers/postman-collection.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/bruno-app/src/utils/importers/postman-collection.js b/packages/bruno-app/src/utils/importers/postman-collection.js index 51575611..4486e0ef 100644 --- a/packages/bruno-app/src/utils/importers/postman-collection.js +++ b/packages/bruno-app/src/utils/importers/postman-collection.js @@ -4,6 +4,7 @@ import { uuid } from 'utils/common'; import { BrunoError } from 'utils/common/error'; import { validateSchema, transformItemsInCollection, hydrateSeqInCollection } from './common'; import { postmanTranslation } from 'utils/importers/translators/postman_translation'; +import each from 'lodash/each'; const readFile = (files) => { return new Promise((resolve, reject) => { From fc79436787dc435d2627357df1bd1430be8ed7c6 Mon Sep 17 00:00:00 2001 From: lohit Date: Sat, 21 Sep 2024 19:49:50 +0530 Subject: [PATCH 48/59] fix: updates (#3158) --- .../EnvironmentSettings/CopyEnvironment/index.js | 2 +- .../EnvironmentSettings/CreateEnvironment/index.js | 2 +- .../EnvironmentSettings/RenameEnvironment/index.js | 2 +- packages/bruno-app/src/components/Modal/index.js | 7 ++++--- .../Collections/Collection/CloneCollection/index.js | 4 ++-- .../CollectionItem/CloneCollectionItem/index.js | 3 ++- .../CollectionItem/RenameCollectionItem/index.js | 13 ++++++++++--- .../Collection/RenameCollection/index.js | 13 +++++++++---- .../components/Sidebar/CreateCollection/index.js | 4 ++-- .../Sidebar/ImportCollectionLocation/index.js | 2 +- .../src/components/Sidebar/NewFolder/index.js | 7 +++++-- .../src/components/Sidebar/NewRequest/index.js | 12 +++++++++--- 12 files changed, 47 insertions(+), 24 deletions(-) diff --git a/packages/bruno-app/src/components/Environments/EnvironmentSettings/CopyEnvironment/index.js b/packages/bruno-app/src/components/Environments/EnvironmentSettings/CopyEnvironment/index.js index a9fdf3b4..586f874b 100644 --- a/packages/bruno-app/src/components/Environments/EnvironmentSettings/CopyEnvironment/index.js +++ b/packages/bruno-app/src/components/Environments/EnvironmentSettings/CopyEnvironment/index.js @@ -44,7 +44,7 @@ const CopyEnvironment = ({ collection, environment, onClose }) => { return ( - + e.preventDefault()}>