diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index a7c8969da..785378f91 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -2,8 +2,12 @@ -# Contribution Checklist: +### Contribution Checklist: +- [ ] **The pull request only addresses one issue or adds one feature.** - [ ] **The pull request does not introduce any breaking changes** +- [ ] **I have added screenshots or gifs to help explain the change if applicable.** - [ ] **I have read the [contribution guidelines](https://github.com/usebruno/bruno/blob/main/contributing.md).** - [ ] **Create an issue and link to the pull request.** + +Note: Keeping the PR small and focused helps make it easier to review and merge. If you have multiple changes you want to make, please consider submitting them as separate pull requests. diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 86a9e0ebd..a2c73beec 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -5,18 +5,16 @@ on: pull_request: branches: [main] jobs: - test: + tests: timeout-minutes: 60 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-node@v3 with: - node-version: 16 - - name: Check package-lock.json - run: npm ci + node-version-file: '.nvmrc' - name: Install dependencies - run: npm i --legacy-peer-deps + run: npm ci --legacy-peer-deps - name: Test Package bruno-query run: npm run test --workspace=packages/bruno-query - name: Build Package bruno-query @@ -33,3 +31,15 @@ jobs: run: npm run test --workspace=packages/bruno-cli - name: Test Package bruno-electron run: npm run test --workspace=packages/bruno-electron + + prettier: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v3 + with: + node-version-file: '.nvmrc' + - name: Install dependencies + run: npm ci --legacy-peer-deps + - name: Run Prettier + run: npm run test:prettier:web diff --git a/contributing.md b/contributing.md index abfcce4d3..966a6134b 100644 --- a/contributing.md +++ b/contributing.md @@ -1,4 +1,4 @@ -**English** | [Русский](/contributing_ru.md) +**English** | [Українська](/contributing_ua.md) | [Русский](/contributing_ru.md) ## Lets make bruno better, together !! diff --git a/contributing_ru.md b/contributing_ru.md index 316408162..6636004ba 100644 --- a/contributing_ru.md +++ b/contributing_ru.md @@ -1,4 +1,4 @@ -[English](/contributing.md) | **Русский** +[English](/contributing.md) | [Українська](/contributing_ua.md) | **Русский** ## Давайте вместе сделаем Бруно лучше!!! @@ -34,4 +34,4 @@ Bruno построен с использованием NextJs и React. Мы т - feature/[название функции]: Эта ветка должна содержать изменения для конкретной функции - Пример: feature/dark-mode - bugfix/[название ошибки]: Эта ветка должна содержать только исправления для конкретной ошибки - - Пример bugfix/bug-1 \ No newline at end of file + - Пример bugfix/bug-1 diff --git a/contributing_ua.md b/contributing_ua.md new file mode 100644 index 000000000..75760f565 --- /dev/null +++ b/contributing_ua.md @@ -0,0 +1,37 @@ +[English](/contributing.md) | **Українська** | [Русский](/contributing_ru.md) + +## Давайте зробимо Bruno краще, разом !! + +Я дуже радий що Ви бажаєте покращити Bruno. Нижче наведені вказівки як розпочати розробку Bruno на Вашому комп'ютері. + +### Стек технологій + +Bruno побудований на NextJs та React. Також для десктопної версії (яка підтримує локальні колекції) використовується Electron + +Бібліотеки, які ми використовуємо + +- CSS - Tailwind +- Редактори коду - Codemirror +- Керування станом - Redux +- Іконки - Tabler Icons +- Форми - formik +- Валідація по схемі - Yup +- Клієнт запитів - axios +- Спостерігач за файловою системою - chokidar + +### Залежності + +Вам знадобиться [Node v18.x або остання LTS версія](https://nodejs.org/en/) та npm 8.x. Ми використовуєм npm workspaces в цьому проекті + +### Починаєм писати код + +Будь ласка, зверніться до [development_ua.md](docs/development_ua.md) за інструкціями щодо запуску локального середовища розробки. + +### Створення Pull Request-ів + +- Будь ласка, робіть PR-и маленькими і сфокусованими на одній речі +- Будь ласка, слідуйте формату назв гілок + - feature/[назва feature]: Така гілка має містити зміни лише щодо конкретної feature + - Приклад: feature/dark-mode + - bugfix/[назва баґу]: Така гілка має містити лише виправлення конкретного багу + - Приклад: bugfix/bug-1 diff --git a/docs/development.md b/docs/development.md index c1c402e08..d56d3e6cf 100644 --- a/docs/development.md +++ b/docs/development.md @@ -1,4 +1,4 @@ -**English** | [Русский](/docs/development_ru.md) +**English** | [Українська](/docs/development_ua.md) | [Русский](/docs/development_ru.md) ## Development diff --git a/docs/development_ru.md b/docs/development_ru.md index 4d4e3a80e..3816066e0 100644 --- a/docs/development_ru.md +++ b/docs/development_ru.md @@ -1,4 +1,4 @@ -[English](/docs/development.md) | **Русский** +[English](/docs/development.md) | [Українська](/docs/development_ua.md) | **Русский** ## Разработка diff --git a/docs/development_ua.md b/docs/development_ua.md new file mode 100644 index 000000000..d6d5bcdf8 --- /dev/null +++ b/docs/development_ua.md @@ -0,0 +1,55 @@ +[English](/docs/development.md) | **Українська** | [Русский](/docs/development_ru.md) + +## Розробка + +Bruno розробляється як декстопний застосунок. Вам потрібно запустити nextjs в одній сесії терміналу, та запустити застосунок Electron в іншій сесії терміналу. + +### Залежності + +- NodeJS v18 + +### Локальна розробка + +```bash +# Використовуйте nodejs 18-ї версії +nvm use + +# встановіть залежності +npm i --legacy-peer-deps + +# зберіть документацію graphql +npm run build:graphql-docs + +# зберіть bruno query +npm run build:bruno-query + +# запустіть додаток next (термінал 1) +npm run dev:web + +# запустіть додаток електрон (термінал 2) +npm run dev:electron +``` + +### Усунення несправностей + +Ви можете зтикнутись із помилкою `Unsupported platform` коли запускаєте `npm install`. Щоб усунути цю проблему, вам потрібно видалити `node_modules` та `package-lock.json`, і тоді запустити `npm install`. Це має встановити всі потрібні для запуску додатку пекеджі. + +```shell +# Видаліть node_modules в піддиректоріях +find ./ -type d -name "node_modules" -print0 | while read -d $'\0' dir; do + rm -rf "$dir" +done + +# Видаліть package-lock в піддиректоріях +find . -type f -name "package-lock.json" -delete +``` + +### Тестування + +```bash +# bruno-schema +npm test --workspace=packages/bruno-schema + +# bruno-lang +npm test --workspace=packages/bruno-lang +``` diff --git a/package-lock.json b/package-lock.json index e8f35901b..a39d0c028 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16717,7 +16717,7 @@ }, "packages/bruno-electron": { "name": "bruno", - "version": "v0.24.0", + "version": "v0.25.0", "dependencies": { "@aws-sdk/credential-providers": "^3.425.0", "@usebruno/js": "0.8.0", diff --git a/package.json b/package.json index 311da85f4..eab3cd9da 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "build:electron:snap": "./scripts/build-electron.sh snap", "test:e2e": "npx playwright test", "test:report": "npx playwright show-report", + "test:prettier:web": "npm run test:prettier --workspace=packages/bruno-app", "prepare": "husky install" }, "overrides": { diff --git a/packages/bruno-app/package.json b/packages/bruno-app/package.json index ea4e9434d..8ead3b925 100644 --- a/packages/bruno-app/package.json +++ b/packages/bruno-app/package.json @@ -8,6 +8,7 @@ "start": "next start", "lint": "next lint", "test": "jest", + "test:prettier": "prettier --check \"./src/**/*.{js,jsx,json,ts,tsx}\"", "prettier": "prettier --write \"./src/**/*.{js,jsx,json,ts,tsx}\"" }, "dependencies": { diff --git a/packages/bruno-app/src/components/CollectionSettings/ProxySettings/index.js b/packages/bruno-app/src/components/CollectionSettings/ProxySettings/index.js index 3f0981f8d..fd3cc8986 100644 --- a/packages/bruno-app/src/components/CollectionSettings/ProxySettings/index.js +++ b/packages/bruno-app/src/components/CollectionSettings/ProxySettings/index.js @@ -1,24 +1,24 @@ import React, { useEffect } from 'react'; import { useFormik } from 'formik'; - +import Tooltip from 'components/Tooltip'; import StyledWrapper from './StyledWrapper'; import * as Yup from 'yup'; import toast from 'react-hot-toast'; const ProxySettings = ({ proxyConfig, onUpdate }) => { const proxySchema = Yup.object({ - enabled: Yup.string().oneOf(['global', 'enabled', 'disabled']), + use: Yup.string().oneOf(['global', 'true', 'false']), protocol: Yup.string().oneOf(['http', 'https', 'socks4', 'socks5']), hostname: Yup.string() - .when('enabled', { - is: 'enabled', + .when('use', { + is: true, then: (hostname) => hostname.required('Specify the hostname for your proxy.'), otherwise: (hostname) => hostname.nullable() }) .max(1024), port: Yup.number() - .when('enabled', { - is: 'enabled', + .when('use', { + is: true, then: (port) => port.required('Specify port between 1 and 65535').typeError('Specify port between 1 and 65535'), otherwise: (port) => port.nullable().transform((_, val) => (val ? Number(val) : null)) }) @@ -26,11 +26,11 @@ const ProxySettings = ({ proxyConfig, onUpdate }) => { .max(65535), auth: Yup.object() .when('enabled', { - is: 'enabled', + is: true, then: Yup.object({ enabled: Yup.boolean(), username: Yup.string() - .when(['enabled'], { + .when('enabled', { is: true, then: (username) => username.required('Specify username for proxy authentication.') }) @@ -44,12 +44,12 @@ const ProxySettings = ({ proxyConfig, onUpdate }) => { }) }) .optional(), - noProxy: Yup.string().optional().max(1024) + bypassProxy: Yup.string().optional().max(1024) }); const formik = useFormik({ initialValues: { - enabled: proxyConfig.enabled || 'global', + use: proxyConfig.use || 'global', protocol: proxyConfig.protocol || 'http', hostname: proxyConfig.hostname || '', port: proxyConfig.port || '', @@ -58,13 +58,20 @@ const ProxySettings = ({ proxyConfig, onUpdate }) => { username: proxyConfig.auth ? proxyConfig.auth.username || '' : '', password: proxyConfig.auth ? proxyConfig.auth.password || '' : '' }, - noProxy: proxyConfig.noProxy || '' + bypassProxy: proxyConfig.bypassProxy || '' }, validationSchema: proxySchema, onSubmit: (values) => { proxySchema .validate(values, { abortEarly: true }) .then((validatedProxy) => { + // serialize 'use' to boolean + if (validatedProxy.use === 'true') { + validatedProxy.use = true; + } else if (validatedProxy.use === 'false') { + validatedProxy.use = false; + } + onUpdate(validatedProxy); }) .catch((error) => { @@ -76,7 +83,7 @@ const ProxySettings = ({ proxyConfig, onUpdate }) => { useEffect(() => { formik.setValues({ - enabled: proxyConfig.enabled || 'global', + use: proxyConfig.use === true ? 'true' : proxyConfig.use === false ? 'false' : 'global', protocol: proxyConfig.protocol || 'http', hostname: proxyConfig.hostname || '', port: proxyConfig.port || '', @@ -85,32 +92,37 @@ const ProxySettings = ({ proxyConfig, onUpdate }) => { username: proxyConfig.auth ? proxyConfig.auth.username || '' : '', password: proxyConfig.auth ? proxyConfig.auth.password || '' : '' }, - noProxy: proxyConfig.noProxy || '' + bypassProxy: proxyConfig.bypassProxy || '' }); }, [proxyConfig]); return (

Proxy Settings

-
-
+ `} + tooltipId="request-var" + />
-
diff --git a/packages/bruno-app/src/components/Preferences/General/index.js b/packages/bruno-app/src/components/Preferences/General/index.js index 2c7bf0228..00e91df7c 100644 --- a/packages/bruno-app/src/components/Preferences/General/index.js +++ b/packages/bruno-app/src/components/Preferences/General/index.js @@ -1,67 +1,103 @@ -import React, { useState } from 'react'; +import React from 'react'; +import { useFormik } from 'formik'; import { useSelector, useDispatch } from 'react-redux'; import { savePreferences } from 'providers/ReduxStore/slices/app'; import StyledWrapper from './StyledWrapper'; +import * as Yup from 'yup'; +import toast from 'react-hot-toast'; const General = ({ close }) => { const preferences = useSelector((state) => state.app.preferences); const dispatch = useDispatch(); - const [sslVerification, setSslVerification] = useState(preferences.request.sslVerification); - const [timeout, setTimeout] = useState(preferences.request.timeout); + const preferencesSchema = Yup.object().shape({ + sslVerification: Yup.boolean(), + timeout: Yup.mixed() + .transform((value, originalValue) => { + return originalValue === '' ? undefined : value; + }) + .nullable() + .test('isNumber', 'Request Timeout must be a number', (value) => { + return value === undefined || !isNaN(value); + }) + .test('isValidTimeout', 'Request Timeout must be equal or greater than 0', (value) => { + return value === undefined || Number(value) >= 0; + }) + }); - const handleSave = () => { + const formik = useFormik({ + initialValues: { + sslVerification: preferences.request.sslVerification, + timeout: preferences.request.timeout + }, + validationSchema: preferencesSchema, + onSubmit: async (values) => { + try { + const newPreferences = await preferencesSchema.validate(values, { abortEarly: true }); + handleSave(newPreferences); + } catch (error) { + console.error('Preferences validation error:', error.message); + } + } + }); + + const handleSave = (newPreferences) => { dispatch( savePreferences({ ...preferences, request: { - sslVerification, - timeout + sslVerification: newPreferences.sslVerification, + timeout: newPreferences.timeout } }) - ).then(() => { - close(); - }); - }; - - const handleTimeoutChange = (value) => { - const validTimeout = isNaN(Number(value)) ? timeout : Number(value); - setTimeout(validTimeout); + ) + .then(() => { + close(); + }) + .catch((err) => console.log(err) && toast.error('Failed to update preferences')); }; return ( -
- - setSslVerification(!sslVerification)} - className="mousetrap mr-0" - /> -
-
- - handleTimeoutChange(e.target.value)} - defaultValue={timeout === 0 ? '' : timeout} - /> -
- -
- -
+ +
+ + +
+
+ + +
+ {formik.touched.timeout && formik.errors.timeout ? ( +
{formik.errors.timeout}
+ ) : null} +
+ +
+
); }; diff --git a/packages/bruno-app/src/components/Preferences/ProxySettings/index.js b/packages/bruno-app/src/components/Preferences/ProxySettings/index.js index d40f98792..6b3fb7877 100644 --- a/packages/bruno-app/src/components/Preferences/ProxySettings/index.js +++ b/packages/bruno-app/src/components/Preferences/ProxySettings/index.js @@ -49,7 +49,7 @@ const ProxySettings = ({ close }) => { }) }) .optional(), - noProxy: Yup.string().optional().max(1024) + bypassProxy: Yup.string().optional().max(1024) }); const formik = useFormik({ @@ -63,7 +63,7 @@ const ProxySettings = ({ close }) => { username: preferences.proxy.auth ? preferences.proxy.auth.username || '' : '', password: preferences.proxy.auth ? preferences.proxy.auth.password || '' : '' }, - noProxy: preferences.proxy.noProxy || '' + bypassProxy: preferences.proxy.bypassProxy || '' }, validationSchema: proxySchema, onSubmit: (values) => { @@ -101,21 +101,21 @@ const ProxySettings = ({ close }) => { username: preferences.proxy.auth ? preferences.proxy.auth.username || '' : '', password: preferences.proxy.auth ? preferences.proxy.auth.password || '' : '' }, - noProxy: preferences.proxy.noProxy || '' + bypassProxy: preferences.proxy.bypassProxy || '' }); }, [preferences]); return ( -

Proxy Settings

+

Global Proxy Settings

-
+
-
+
@@ -166,7 +166,7 @@ const ProxySettings = ({ close }) => {
-
+
@@ -186,7 +186,7 @@ const ProxySettings = ({ close }) => {
{formik.errors.hostname}
) : null}
-
+
@@ -206,7 +206,7 @@ const ProxySettings = ({ close }) => {
{formik.errors.port}
) : null}
-
+
@@ -218,7 +218,7 @@ const ProxySettings = ({ close }) => { />
-
+
@@ -238,7 +238,7 @@ const ProxySettings = ({ close }) => {
{formik.errors.auth.username}
) : null}
-
+
@@ -259,24 +259,24 @@ const ProxySettings = ({ close }) => { ) : null}
-
-