Compare commits

..

381 Commits

Author SHA1 Message Date
Anoop M D
f9f1ca0640 chore: version bump 0.22.0 2023-10-09 07:30:37 +05:30
Anoop M D
53eb53e062 chore: collection and folder as request names are reserved 2023-10-09 07:08:36 +05:30
Anoop M D
2d149d94ef Merge pull request #456 from survivant/feature/request-indexname
Feature/request indexname
2023-10-09 07:00:50 +05:30
Anoop M D
f0c3b8a877 Merge pull request #467 from game5413/enhancement/collection-dropdown
Better experience for open request or folder dropdown action
2023-10-09 06:52:13 +05:30
Anoop M D
3a222458d1 Merge pull request #468 from rqbazan/feat/add-support-for-socks5-proxy
feat: add support for socks5 proxy
2023-10-09 06:40:32 +05:30
Anoop M D
46bc097733 Merge branch 'main' into feat/add-support-for-socks5-proxy 2023-10-09 06:40:12 +05:30
Anoop M D
1ce8d707f1 feat(#334): collection level headers, auth, scripts and tests 2023-10-09 06:18:05 +05:30
Ricardo Q. Bazan
bf90ee96e5 chore: add support for cli 2023-10-08 12:09:33 -05:00
Ricardo Q. Bazan
ce2e997c06 feat: add support for socks5 proxy 2023-10-08 10:04:16 -05:00
game5413
7617ae12cb Merge branch 'main' into enhancement/collection-dropdown 2023-10-08 21:48:36 +07:00
game5413
905e993a13 better experience for open dropdown collection 2023-10-08 21:46:13 +07:00
Anoop M D
159dd90b03 fix: fixed issue where window width and height were not set for first time users 2023-10-08 05:26:49 +05:30
Anoop M D
c222bf47c3 Merge pull request #452 from jarne/feature/persist-window-state
Persist window state (size, position)
2023-10-08 04:36:09 +05:30
Anoop M D
c91bc0d1c6 Merge pull request #457 from Sl-Alex/bugfix/between-assert
Fix 'between' assertion
2023-10-08 04:32:12 +05:30
Anoop M D
64ac763a13 Merge pull request #441 from grubersjoe/gql-auth
Improve GQL introspection request
2023-10-08 04:17:00 +05:30
Anoop M D
bca3d5749b Merge pull request #447 from therealrinku/two
minor styling fixes
2023-10-08 04:15:33 +05:30
Anoop M D
2ee6759282 Merge pull request #455 from survivant/patch-1
typo in the error message
2023-10-08 04:10:58 +05:30
Anoop M D
c479c2e786 Merge pull request #459 from lared/feature/show-devtools
fix(#440) open dev tools from menu
2023-10-08 04:10:16 +05:30
Michał Szymborski
6d8bdfa276 fix(#440) open dev tools from menu 2023-10-07 23:42:12 +02:00
Oleksii Slabchenko
024f61a95e Fix 'between' assertion 2023-10-07 22:42:13 +02:00
Anoop M D
856236c918 feat(#334): Bru lang updates for collection.bru file 2023-10-08 01:57:30 +05:30
Sebastien Dionne
0179c58cd2 fix typo and use singular 2023-10-07 15:54:33 -04:00
Sebastien Dionne
515381b930 fix typo and use singular 2023-10-07 15:53:34 -04:00
Sebastien Dionne
5ef0de2e4d Revert "Building electron script in js to support different OS"
This reverts commit ae78ed36ef.
2023-10-07 15:33:38 -04:00
Anoop M D
c6fef2f1be chore: removed legacy code 2023-10-08 01:01:40 +05:30
Sebastien Dionne
21536f9a27 Support request name with "index" in the name if index is not separated 2023-10-07 15:28:17 -04:00
Anoop M D
faf8581ddf feat(#253): hide hotkey 2023-10-08 00:55:09 +05:30
Sebastien Dionne
47992288c4 Use singular instead 2023-10-07 15:04:45 -04:00
Sebastien Dionne
640692abc7 typo in the error message
there was a little typo
2023-10-07 14:59:55 -04:00
Anoop M D
ed95b8349e chore: bump version v0.21.1 2023-10-08 00:05:10 +05:30
Anoop M D
81b1982d4f feat: response overlay polish 2023-10-08 00:03:32 +05:30
Anoop M D
e381d0c00b chore: ui layout polish 2023-10-07 19:12:20 +05:30
Anoop M D
50f2f54335 fix(#150): fixed arrow direction 2023-10-07 19:10:11 +05:30
Jarne
fab350a32d Add some checks if position/size is inside screen border 2023-10-07 14:51:47 +02:00
Jarne
4600217606 Add basic window state persistance 2023-10-07 14:27:24 +02:00
therealrinku
6421148dc1 minor styling fixes 2023-10-07 12:50:19 +05:45
Anoop M D
12f3f802a7 request save control 2023-10-07 09:02:19 +05:30
Sebastien Dionne
ae78ed36ef Building electron script in js to support different OS 2023-10-06 22:17:10 -04:00
Anoop M D
1e27427d09 chore: bump version to 0.21.0 2023-10-07 03:28:11 +05:30
Jonathan Gruber
f2a8bd5d10 chore: update package-lock.json 2023-10-06 23:57:50 +02:00
Anoop M D
860a3b16ad fix(#440): bring back menu bar 2023-10-07 03:26:12 +05:30
Anoop M D
8fadaf98ae feat(#306): bru cli v0.13.0 release 2023-10-07 03:23:39 +05:30
Anoop M D
8f1f41374c feat(#306): module whitelisting support 2023-10-07 03:20:44 +05:30
Anoop M D
e3679c9ee9 feat(#306): module whitelisting support 2023-10-07 03:19:02 +05:30
Jonathan Gruber
7f17999486 fix(#354): add set headers to introspection request 2023-10-06 23:48:40 +02:00
Jonathan Gruber
d2b35beb6f fix(#354): use Handlebars instead of deprecated Mustache 2023-10-06 23:23:57 +02:00
Anoop M D
0f3a8a87bb feat(#306): bru cli support for allowScriptFilesystemAccess 2023-10-07 02:41:56 +05:30
Jonathan Gruber
3c0770b792 fix(#354): also interpolate env vars in introspection request 2023-10-06 23:10:53 +02:00
Anoop M D
5a89d12716 feat: removed legacy v1 bru lang auto migrate functionality 2023-10-07 00:25:26 +05:30
Anoop M D
8730c5a85b Merge pull request #413 from therealrinku/one
request colors made consistent on the sidebar and tabs
2023-10-06 23:34:08 +05:30
Anoop M D
24fcb8caf2 Merge pull request #430 from lared/bugfix/misordered-requests
fix(#428): moving requests into directories or into root of collection broke ordering
2023-10-06 22:58:08 +05:30
Anoop M D
5a0e68073f Merge pull request #436 from not-known-person/fix-/#419
fix-/#419
2023-10-06 22:56:17 +05:30
not-known-person
945f1eb74a fix-/#419 2023-10-06 22:00:20 -04:00
Anoop M D
de74edb50f feat: interpolation of proxy vars 2023-10-06 22:49:48 +05:30
Anoop M D
9474918853 chore: fixed typo 2023-10-06 22:11:14 +05:30
Anoop M D
6db36513c5 Merge pull request #434 from oscarpas/fix/graphql-docs-explorer-styling
fix: add missing CSS for GraphQL docs explorer
2023-10-06 21:43:57 +05:30
Oscar Pastarus
7be38bcfe0 fix: add missing CSS for graphql docs explorer 2023-10-06 17:46:09 +02:00
Michał Szymborski
1f5aa25d5e fix(#428): moving requests into directories or into root of collection broke ordering 2023-10-06 16:39:13 +02:00
Anoop M D
65e448b1eb Merge pull request #374 from acostinescu/main
Changed Proxied Requests to Use https-proxy-agent/http-proxy-agent.
2023-10-06 17:55:39 +05:30
Anoop M D
d5a6522563 Merge branch 'main' into main 2023-10-06 17:36:35 +05:30
therealrinku
be72d24ecb request colors made consistent on the sidebar and tabs 2023-10-06 14:57:20 +05:45
Anoop M D
c25542bbdf chore: bru cli v0.12.0 release 2023-10-06 04:59:57 +05:30
Anoop M D
a838185ddf chore: version bump v0.20.0 2023-10-06 04:18:44 +05:30
Anoop M D
ac3637fcfa Merge pull request #404 from qweme32/feature/russian-localization
Add Russian localization
2023-10-06 04:15:22 +05:30
Anoop M D
e3f32dfc87 Merge pull request #397 from dozed/response-time-axios
Use axios interceptor to measure response time
2023-10-06 04:13:52 +05:30
Anoop M D
a204e3814b Merge pull request #400 from lared/bug/env-with-empty-secret-variable
fix(#399): loading environment with empty secret variable fails
2023-10-06 04:11:57 +05:30
Anoop M D
be8db1876a feat(#111): refactor new request auto open logic 2023-10-06 04:02:51 +05:30
Qweme Dev
64d9413777 Add Russian localization 2023-10-05 22:47:14 +03:00
Michał Szymborski
a71afc8f73 fix(#399): loading environment with empty secret variable fails 2023-10-05 19:38:17 +02:00
Anoop M D
4e4130acf5 Merge pull request #373 from Its-treason/feature/auto-open-new-request
feat(#111): Automaticly open a newly created request
2023-10-05 21:22:56 +05:30
Stefan Ollinger
bb7d13d2d9 Use axios interceptor to measure response time 2023-10-05 17:46:12 +02:00
Anoop M D
c08be14d87 Merge branch 'main' of github.com:usebruno/bruno 2023-10-05 21:15:59 +05:30
Anoop M D
90f47e5877 feat(#396): decomment support in json and scripts 2023-10-05 21:15:38 +05:30
Its-treason
8a19189dcd feat(#111): Use existing last actions for auto open tabs 2023-10-05 17:43:17 +02:00
Anoop M D
efbc119e7c Merge pull request #395 from dozed/fix-show-response-time
Put constant in outer scope
2023-10-05 20:50:00 +05:30
Anoop M D
d36956e0a4 Merge pull request #393 from zyrouge/linux-qol
Better BrowserWindow
2023-10-05 20:41:31 +05:30
Stefan Ollinger
efbdde8252 Set responseTime 2023-10-05 17:11:29 +02:00
Stefan Ollinger
ae2f170fe6 Put constant in outer scope 2023-10-05 17:05:31 +02:00
zyrouge
4f0d87fba4 refactor: set title 2023-10-05 20:29:50 +05:30
zyrouge
8703124007 refactor: enable auto hide menu bar 2023-10-05 20:24:02 +05:30
zyrouge
f9f99adbf9 refactor: set icon of windows 2023-10-05 20:23:33 +05:30
Anoop M D
da9a6c434a Merge pull request #390 from grubersjoe/gql-auth
feat: Add authentication for GraphQL requests
2023-10-05 20:21:58 +05:30
Anoop M D
c8764f6555 Merge pull request #391 from dozed/show-response-time
Show response time in milliseconds per request and total
2023-10-05 20:17:58 +05:30
Stefan Ollinger
e90718f47b Show response time in milliseconds per request and total 2023-10-05 16:42:49 +02:00
Jonathan Gruber
af130bac17 feat(usebruno#354): Add authentication for GraphQL requests
Schema introspection query will also use specified authentication.
2023-10-05 16:37:05 +02:00
Stefan Ollinger
04481a26e1 Show response time in milliseconds per request and total 2023-10-05 16:36:57 +02:00
Anoop M D
86c2a60184 Merge pull request #380 from petoc/bugfix/insomnia-importer
updated insomnia importer
2023-10-05 19:54:06 +05:30
Anoop M D
0cbf803ed9 chore: restructured tests into seperate dir as jest error was preventing local exec of cli 2023-10-05 19:51:06 +05:30
Anoop M D
c91819c072 Merge branch 'main' of github.com:usebruno/bruno 2023-10-05 19:40:44 +05:30
Anoop M D
2c4a3a5eb4 Merge pull request #241 from tpyle/bug/correct-result-reporting
Handle failed requests and reduce duplication
2023-10-05 19:40:28 +05:30
Anoop M D
99fc980a48 chore: updated readme 2023-10-05 19:30:38 +05:30
Anoop M D
960f62ac8e chore: updated readme 2023-10-05 15:44:34 +05:30
Peter C.
57e0f0c0c4 updated insomnia importer 2023-10-05 09:45:08 +02:00
Alex Costinescu
8216bf5eec Merge remote-tracking branch 'upstream/main' 2023-10-04 20:37:18 -04:00
Alex Costinescu
9f11cfc836 Changed proxied requests to use https-proxy-agent to handle TCP tunneling. 2023-10-04 20:31:34 -04:00
Its-treason
51cb930b6a feat(#111): Automaticly open a newly created request 2023-10-05 01:57:02 +02:00
Anoop M D
1c8712b6eb chore: bru cli v0.11.0 release 2023-10-05 04:04:05 +05:30
Anoop M D
6c1f8c78b2 chore: version bumped to v0.19.0 2023-10-05 03:47:43 +05:30
Anoop M D
b35b814561 chore: added package-lock.json 2023-10-05 03:46:34 +05:30
Anoop M D
80eaaad658 Merge pull request #356 from therealrinku/main
github star button color fix
2023-10-05 03:35:38 +05:30
Anoop M D
51a73d864e Merge pull request #368 from luizfonseca/fix/use-correct-duration-axios-interceptor
fix(network): use axios interceptors to better reflect durations
2023-10-05 03:34:34 +05:30
Anoop M D
87e29db545 Merge pull request #370 from VersusF/feature/copy-environment
Feature: Add new button to copy existing environments
2023-10-05 03:29:31 +05:30
Anoop M D
0cf18e6804 feat(#306): allow fs access inside scripts 2023-10-05 03:20:53 +05:30
Filippo Contro
3a6dacc1f4 Add: Add new button to copy existing environments 2023-10-04 23:32:02 +02:00
Anoop M D
8d89496304 fix(#329): fixed basic auth header issue + added cli support for basic and bearer auth 2023-10-05 02:32:24 +05:30
Luiz Fonseca
6ef7891f8a fix(network): use interceptors when measuring request duration 2023-10-04 22:09:48 +02:00
Luiz Fonseca
e1bf475f1f chore: pretty-quick fixes 2023-10-04 22:09:11 +02:00
Luiz Fonseca
f7eb325e11 fix: husky pre-commit missing correct prettier version 2023-10-04 22:08:25 +02:00
Luiz Fonseca
c50b88d60d fix: missing typescript module on npm build script 2023-10-04 22:07:26 +02:00
Anoop M D
90fedc8ec6 fix(#334): fixed yaml response parsing issue 2023-10-04 22:55:17 +05:30
therealrinku
9c46bc79d9 github star button color fix 2023-10-04 22:14:34 +05:45
Anoop M D
9ecfb3a640 chore: added testimnoial link in readme 2023-10-04 18:14:45 +05:30
Thomas Pyle
da4e96d1ef Merge remote-tracking branch 'upstream/main' into bug/correct-result-reporting 2023-10-03 22:15:53 -04:00
Anoop M D
bf2d1220a1 chore: version bumped to v0.18.0 2023-10-04 05:53:59 +05:30
Anoop M D
744abe585c revert pr #252 2023-10-04 05:52:59 +05:30
Anoop M D
5e2640fe73 fix: fixed query result issue 2023-10-04 05:48:05 +05:30
Anoop M D
7513314179 Merge branch 'main' of github.com:usebruno/bruno 2023-10-04 04:20:52 +05:30
Anoop M D
d4b663bfa8 Merge pull request #252 from acostinescu/main
Changed Proxied Requests to Use https-proxy-agent.
2023-10-04 04:20:38 +05:30
Anoop M D
9b2e2ed3d8 chore: style updates 2023-10-04 04:11:23 +05:30
Anoop M D
e4ea6b9109 Merge pull request #299 from ilumin/feature/double-click-rename
feat(291): add double click request to open rename modal
2023-10-04 03:40:29 +05:30
Anoop M D
1a8feb8029 Merge pull request #282 from Its-treason/feature/preview-response-html
feature/preview-response-html
2023-10-04 03:40:11 +05:30
Anoop M D
72a5f05009 Merge pull request #302 from j0k3r/bugfix/reponse-size-two-decimal
Fix rounding response size
2023-10-04 03:32:30 +05:30
Anoop M D
d5ef240de6 Merge pull request #308 from Its-treason/bugfix/assert-not-being-removed
fix(#270): Fix deleted assertions & tests shown in test tab
2023-10-04 03:29:44 +05:30
Anoop M D
1932d98b57 Merge pull request #321 from lared/bugfix/tabbing-order
fix(#314): tabbing order through tables (variables etc) was inconsistent or impossible
2023-10-04 03:26:00 +05:30
Anoop M D
bdb7ff9947 chore: style updates 2023-10-04 03:07:50 +05:30
Anoop M D
3f4f7fb24c feat(#119): basic auth support completed 2023-10-04 02:58:22 +05:30
Michał Szymborski
494b484cd9 fix(#312): tabbing order through tables (variables etc) was inconsistent or impossible
resolves #312
2023-10-03 21:36:31 +02:00
Anoop M D
48e4e60696 feat(#119): bearer auth support completed 2023-10-04 00:36:52 +05:30
Anoop M D
2e600838b2 Merge branch 'main' of github.com:usebruno/bruno 2023-10-03 22:48:56 +05:30
Anoop M D
52428c6b35 Merge pull request #318 from usebruno/feature/auth-support
Feature/auth support
2023-10-03 22:47:22 +05:30
Its-treason
2734f26c78 fix(#270): Fix deleted assertions & tests shown in test tab
After all tests or assertions were deleted the store was not
updated because of the length checks. Now the assertions/tests
will always try to run, to ensure the data is always updated.
2023-10-03 13:04:34 +02:00
Jeremy Benoist
73c62010b7 Fix rounding response size
I noticed that sometimes the response size is weirdly displayed.
- size `112932` is displayed as `110.28.999999999999996KB`
- size `112990` is displayed as `110.34KB`

The problem is in the decimal calculation. Rounding the value ensure we don't have two `.` in the displayed size.

Also, update the `contributing` to increase the minimum Node version required.
2023-10-03 10:39:38 +02:00
O Teerasak Vichadee
26853787da feat(291): add double click request to open rename modal 2023-10-03 11:28:27 +07:00
Anoop M D
712319ff34 Merge pull request #296 from Its-treason/bugfix/modal-text-color
fix: Color in environment delete modal
2023-10-03 04:36:21 +05:30
Its-treason
7a80247fc5 fix: Color in environment delete modal 2023-10-03 00:48:49 +02:00
Its-treason
ea9e294c54 feat(#245): Update tab design + Remove CSP 2023-10-02 19:50:01 +02:00
Anoop M D
55315b5b88 Merge pull request #288 from Cibico99/feature/xml-format-option-fix
Fix for XML Formatting Options
2023-10-02 20:34:10 +05:30
pedward99
d63e7371fe Fix for XML Formatting Options 2023-10-02 10:39:29 -04:00
Its-treason
dce11d1bd5 Merge branch 'main' of github.com:usebruno/bruno into feature/preview-response-html 2023-10-02 14:34:54 +02:00
Its-treason
e720fed63b feat(#245): Add HTML preview to response 2023-10-02 14:26:24 +02:00
Anoop M D
43f7c2ab86 chore: version bump to v0.17.0 2023-10-02 16:55:22 +05:30
Anoop M D
95532102ba feat(#280): code generator polishing and support url interpolation 2023-10-02 16:52:02 +05:30
Anoop M D
4603ec4d5e Merge pull request #280 from Beedhan/feature/code-generator
Feature/code generator
2023-10-02 15:42:07 +05:30
Anoop M D
cbdd56e577 chore: added some todo's for future code cleanup 2023-10-02 15:38:50 +05:30
Anoop M D
ce77d08408 Merge branch 'main' of github.com:usebruno/bruno 2023-10-02 15:32:39 +05:30
Anoop M D
3a5071412e Merge pull request #277 from not-known-person/feat-/#220-Improvement
Improved feat(#220)
2023-10-02 15:32:26 +05:30
not-known-person
336ad38cb9 Improved feat-/#220 2023-10-02 08:34:39 -04:00
Anoop M D
636e14a385 feat(#253): released cmd+h key back to native mac hide behaviour 2023-10-02 15:28:54 +05:30
Anoop M D
bfc03f5ae4 fix(#279): fixed codemirror crashes resulting in white screen 2023-10-02 15:25:59 +05:30
Beedhan
97200961eb feat: support for variables in url 2023-10-02 14:24:13 +05:45
Anoop M D
7297bb184e chore: updated readme 2023-10-02 13:58:09 +05:30
Beedhan
b74922c8f3 bugfix:sidebar moving when code generator modal is opened 2023-10-02 14:02:54 +05:45
Anoop M D
ce0827308f chore: updated readme 2023-10-02 13:47:14 +05:30
Beedhan
3d0c9cc0ae feat:added code generator to http-requests 2023-10-02 13:58:25 +05:45
Anoop M D
1804454ff0 chore: bump version v0.16.6 2023-10-02 05:26:47 +05:30
Anoop M D
3c710120b9 Merge pull request #274 from lared/bugfix/drag-and-drop-ordering
fix(#154): correct ordering during drag-and-drop
2023-10-02 04:48:43 +05:30
Anoop M D
fcc12fb089 fix(#251, #265): phantoms folders fix on rename/delete needs to be run only on windows 2023-10-02 04:17:35 +05:30
Anoop M D
3d8dee944f Merge pull request #266 from not-known-person/fix-251-265
fixed issue-#251-&-#265,
2023-10-02 04:03:31 +05:30
Anoop M D
78e5cd3c03 Merge pull request #264 from Its-treason/bugfix/insomnia-import-name-collision
fix(#257): Name collision during Insomnia collection import
2023-10-02 03:37:40 +05:30
Its-treason
26d99c7aee fix(#257): Name collision during Insomnia collection import 2023-10-01 23:47:43 +02:00
Anoop M D
77a7318dfb Merge pull request #272 from not-known-person/feat/-#220
added feat/-#220
2023-10-02 02:55:24 +05:30
not-known-person
b83da46f12 added feat/-#220 2023-10-01 21:01:39 -04:00
not-known-person
cf6ec4e84f fixed issue-#251-&-#265 2023-10-01 15:22:05 -04:00
Michał Szymborski
978d810473 fix(#154): correct ordering during drag-and-drop
When dragging and dropping items, to delay changing sequence numbers
until after all the resource-dependent logic has completed, we were
relying on the order of children in redux store (which we then converted
into new seq numbers).

This order of children was however not updated when sequence numbers
changed (for example due to file watch changes). This resulted in a
seemingly random drag-and-drop ordering, which in fact was linked to the
initial order when the collection was loaded.

This change sorts all the items by sequence number prior to reordering,
so that those random jumps no longer happen. As this happens on a deep
clone of the collection, no data gets hurt in the process.

fixes #154
2023-10-01 21:09:11 +02:00
Beedhan
cedcd2cf35 Revert "fix: folder showing after deleting"
This reverts commit ce9cdc5293.
2023-10-02 00:00:03 +05:45
Beedhan
ce9cdc5293 fix: folder showing after deleting 2023-10-01 23:55:39 +05:45
Anoop M D
e83c2da798 chore: version bumped to v0.16.5 2023-10-01 02:17:28 +05:30
Anoop M D
39f148267e Merge pull request #262 from mirkogolze/feature/about-window
#203 #254 #231 add icon to asar content, change styling to allow copying the content
2023-10-01 01:51:32 +05:30
Mirko Golze
f4f093d4db #203 #254 #231
add icon to asar content, change styling to allow copying the content
2023-09-30 21:56:21 +02:00
Anoop M D
3c4ef2f2df Merge pull request #256 from brahma-dev/main
Ensure that adjacent variables are rendered in separate spans.
2023-10-01 00:19:46 +05:30
Anoop M D
e39975cb3c Merge pull request #259 from sadn1ck/fix/gql-docs-build-error
fix(graphql-docs/build): rollup error thrown during build
2023-10-01 00:12:12 +05:30
Anoop M D
b767ccd063 Merge pull request #260 from Its-treason/feature/update-create-collection-form
feat: Update create collection form
2023-09-30 22:09:54 +05:30
Anoop M D
c5adfd8975 Merge pull request #261 from andypiper/bugfix/grammar-typo
Remove rogue apostrophes and capitalise API in text.
2023-09-30 22:05:01 +05:30
Andy Piper
6695d90609 Remove rogue apostrophes and capitalise API in text.
Signed-off-by: Andy Piper <andypiper@users.noreply.github.com>
2023-09-30 15:37:45 +01:00
Its-treason
5c79282a1b feat: Update create collection form
- Remove Name tooltip
- Update Folder Name tooltip
- Move Folder Name input under location
- Update Folder Name validation
  - Now only allow characters for valid system folder names
- Update label htmlFor ids to input ids
2023-09-30 14:35:37 +02:00
Anik Das
64019f8ecf fix(graphql-docs/build): rollup error thrown during build
- during the dts transformation, the css import was not recognized, hence marking it as external in the dts transform didn't throw the error

- "extract: true" in postcss plugin makes sure it gets extracted to the final bundle as well

Signed-off-by: Anik Das <anikdas0811@gmail.com>
2023-09-30 09:49:02 +05:30
Brahma Dev
4c89f31934 Decrease likelihood of any unintentional classname clash. 2023-09-29 21:58:46 +00:00
Brahma Dev
1c53ce91f0 Ignore the randomly generated classname. 2023-09-29 21:42:15 +00:00
Brahma Dev
0b83fbb7ce Add a randomly generated classname to each variable so that CodeMirror does not merge adjacent variables into the same SPAN. 2023-09-29 21:41:54 +00:00
Anoop M D
08ceed86a8 chore: bump version to v0.16.4 2023-09-30 02:48:20 +05:30
Anoop M D
f99918d725 fix(#229): fixed handling of non-JSON/XML content types 2023-09-30 02:44:30 +05:30
Anoop M D
1877dd858e chore: remove unused var 2023-09-30 02:42:10 +05:30
Anoop M D
acff0c379e fix(#236): insomnia import fix 2023-09-30 02:24:31 +05:30
Anoop M D
a849e4fb7b Merge pull request #255 from Its-treason/bugfix/create-collection-modal-validation
Bugfix: Create collection modal validation
2023-09-30 01:53:32 +05:30
Anoop M D
613699fb69 fix(#248): gracefully abort cm header hint when word match fails 2023-09-30 01:44:54 +05:30
Anoop M D
0c4ba71922 feat(#229): added error boundary to capture error trace and allow users to continue using the app 2023-09-30 01:32:05 +05:30
Its-treason
d346bb00eb Merge branch 'main' into bugfix/create-collection-modal-validation 2023-09-29 21:45:01 +02:00
Its-treason
8bb57aa41d fix: location validation in create collection form 2023-09-29 21:44:37 +02:00
Anoop M D
f378f04fc3 Merge pull request #248 from brahma-dev/main
Autocomplete
2023-09-30 00:35:22 +05:30
Alex Costinescu
742d69b03c Re-added missing https require statement. 2023-09-29 15:37:39 +02:00
Alex Costinescu
0e3bc62d9d Changed proxied requests to use https-proxy-agent to handle TCP tunneling. 2023-09-29 15:24:13 +02:00
Brahma Dev
a02d2b9c58 Add standard http headers for autocomplete 2023-09-29 11:59:09 +00:00
Brahma Dev
21edfbc25a Use Codemirror Hint feature for autocomplete 2023-09-29 11:59:01 +00:00
Thomas Pyle
4ba4d8fc27 Merge branch 'main' into bug/correct-result-reporting 2023-09-29 06:53:57 -05:00
Anoop M D
45042cd52a Merge pull request #243 from LesageYann/feat/allow-async-tests
feat: allow test to be asynchrone
2023-09-29 12:42:57 +05:30
Lesage Yann
314e8c17d3 feat: allow test to be asynchrone 2023-09-29 08:55:26 +02:00
Anoop M D
69a7c0e4ce chore: bumped release version to v0.16.3 2023-09-29 12:24:17 +05:30
Anoop M D
626d925ad6 Merge pull request #242 from tpyle/bug/handle-collection-names-with-slashes
Corrects issue when collection names in imports have slashes
2023-09-29 12:20:11 +05:30
Thomas Pyle
2c0ccf769c Corrects issue when collection names in imports have slashes 2023-09-28 21:42:46 -04:00
Thomas Pyle
5f32924ddb Adds some simple unit tests around printRunSummary 2023-09-28 20:58:25 -04:00
Thomas Pyle
9bcf56d689 Handle failed requests and reduce duplication 2023-09-28 20:42:48 -04:00
Anoop M D
516411b9a2 Merge pull request #240 from gkohen/main
Make sure path string does not overflow the dialog
2023-09-29 02:29:01 +05:30
Anoop M D
0f1e330dc5 feat(#119): ui placeholders for basic and bearer auths 2023-09-29 02:11:04 +05:30
Gabriel Kohen
60e3f3bb6a Make sure path string does not overflow the dialog
See also: #217
2023-09-28 16:22:40 -04:00
Anoop M D
51ee37cf96 feat(#119): bru lang support for basic and bearer auth 2023-09-29 01:35:22 +05:30
Anoop M D
a6b19605b5 Merge pull request #238 from jsoref/spelling
Spelling
2023-09-29 00:25:43 +05:30
Josh Soref
7ba471f26a spelling: serialization
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-09-28 14:11:49 -04:00
Josh Soref
f23dcf50a4 spelling: separator
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-09-28 14:11:49 -04:00
Josh Soref
86cda2cf5a spelling: sample
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-09-28 14:11:49 -04:00
Josh Soref
00b6e007af spelling: people
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-09-28 14:11:49 -04:00
Josh Soref
7313d1b4d7 spelling: occurred
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-09-28 14:11:49 -04:00
Josh Soref
8f803234ce spelling: javascript
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-09-28 14:11:49 -04:00
Josh Soref
76a743b74e spelling: interpreted
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-09-28 14:11:49 -04:00
Josh Soref
c623aa0909 spelling: header
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-09-28 14:11:49 -04:00
Josh Soref
3eb26834c7 spelling: github
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-09-28 14:11:49 -04:00
Josh Soref
64a5852227 spelling: evaluated
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-09-28 14:11:49 -04:00
Josh Soref
6471ca74c3 spelling: ephemeral
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-09-28 14:11:49 -04:00
Josh Soref
f77d955839 spelling: environments
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-09-28 13:24:00 -04:00
Josh Soref
9947a55b8d spelling: bottom
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-09-28 13:24:00 -04:00
Josh Soref
a71555725c spelling: being
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-09-28 13:24:00 -04:00
Anoop M D
c9ec6902a5 Merge pull request #234 from brahma-dev/main
Allow tabs in tablists to wrap.
2023-09-28 18:58:10 +05:30
Brahma Dev
c9c675e187 Allow tabs in tablists to wrap. 2023-09-28 13:11:21 +00:00
Anoop M D
0517b2685e fix(#233): bru cli fix for content header parsing issue 2023-09-28 18:31:42 +05:30
Anoop M D
5d01c0a765 chore: bump version to 0.16.2 2023-09-28 18:25:15 +05:30
Anoop M D
f3925923c9 fix(#233): fixed content type env var parsing issue 2023-09-28 18:24:10 +05:30
Anoop M D
6facdfd66b chore: bump version to v0.16.1 2023-09-28 10:27:45 +05:30
Anoop M D
0f211131b1 feat(#224): proxy config support in collection runner 2023-09-28 10:20:31 +05:30
Anoop M D
cd3b8a948e fix(#227): fixed json formatting issue 2023-09-28 10:15:54 +05:30
Anoop M D
f695036721 feat(#224): Bru CLI support for proxying requests 2023-09-28 05:26:09 +05:30
Anoop M D
3661fa7df3 chore: published libs 2023-09-28 05:17:59 +05:30
Anoop M D
559fcb0806 Merge pull request #225 from mirkogolze/feature/191-interpolate-header-names
#191 interpolate header names with variables
2023-09-28 04:38:37 +05:30
Anoop M D
d5da8a9e2f chore: bump version to v0.16.0 2023-09-28 04:34:34 +05:30
Anoop M D
a3050db6c4 fix(#216): fixed issue where .env vars were not injected into bru.getEnvVar() 2023-09-28 04:32:07 +05:30
Anoop M D
c27f090583 feat(#95): runner runs inside a tab of a collection view 2023-09-28 04:02:20 +05:30
Anoop M D
487dd73040 fix: fixed screen crash when collection was removed 2023-09-28 03:26:13 +05:30
Anoop M D
665428a2d0 feat(#224): proxy support feature - gui layer 2023-09-28 03:06:53 +05:30
Mirko Golze
6a2ba0f746 try other way to retrieve icon path for about window 2023-09-27 22:39:22 +02:00
Mirko Golze
36f9902f2e #191 interpolate header names with variables 2023-09-27 22:36:27 +02:00
Anoop M D
c0b7dad030 feat(#224): proxy support feature - electron layer 2023-09-28 00:58:05 +05:30
Anoop M D
8780d309ac feat: exposing chai library in script and test runtimes 2023-09-27 23:47:56 +05:30
Anoop M D
08c1563a7a chore: bump version to v0.15.3 2023-09-27 14:37:13 +05:30
Anoop M D
07ad1f9f60 fix(#217): Merge pull request #218 from tpyle/bug/no-environments
Adds fallback when no environments are defined
2023-09-27 14:35:26 +05:30
Thomas Pyle
8df6b241bb Adds fallback when no environments are defined 2023-09-26 19:39:46 -04:00
Anoop M D
50e0558d7d Merge pull request #215 from Cibico99/feature/XML-Format
Feature/xml format
2023-09-26 22:58:33 +05:30
Anoop M D
cbe84cc512 Merge pull request #213 from BrentShikoski/feature/license_all_modules
Add license to published npm modules.
2023-09-26 22:50:18 +05:30
Anoop M D
cbb975d81d Merge branch 'main' into feature/license_all_modules 2023-09-26 22:49:19 +05:30
Anoop M D
30ee472c40 release(#212): bru cli v0.9.0 2023-09-26 22:37:55 +05:30
Anoop M D
c7aecbea79 Merge pull request #212 from tpyle/feature/output-collection
Adds an option to collect output from cli runs
2023-09-26 22:09:57 +05:30
pedward99
b814c84411 Clean Up 2023-09-26 09:22:41 -04:00
Brent Shikoski
6306ad17c3 Add license information to modules. 2023-09-25 20:57:51 -05:00
Brent Shikoski
4b800e30e4 Merge branch 'usebruno:main' into feature/add_license_to_all_cli_dependent_modules 2023-09-25 19:37:10 -05:00
Thomas Pyle
89f418a114 Adds an option to collect output from cli runs 2023-09-25 17:48:53 -04:00
pedward99
9c8ef09d01 XML Format working 2023-09-25 13:13:14 -04:00
Brent Shikoski
83d354c25c Add license to modules the cli is dependent on.
- bruno-js
- bruno-lang
- bruno-query
2023-09-24 22:29:10 -05:00
Anoop M D
bb31ddc5d2 chore: release v0.15.2 2023-09-25 04:42:41 +05:30
Anoop M D
ff40178c8c fix(#210): fixing bruno libraries dep issues 2023-09-25 04:41:39 +05:30
Anoop M D
1c549f7faf fix: fixed issue related about-window dep breaking build 2023-09-25 02:54:36 +05:30
Anoop M D
eb6b75ff98 feat(#199): bru cli updates to load .env vars 2023-09-25 02:10:12 +05:30
Anoop M D
eb010adeac chore: added collection variables feature note 2023-09-25 01:09:25 +05:30
Anoop M D
7e5e22cfcf chore: release v0.15.0 2023-09-25 00:58:24 +05:30
Anoop M D
2515e78a10 feat(#200): req.setMaxRedirects() api 2023-09-25 00:09:29 +05:30
Anoop M D
511854369f feat(#205): collection properties dropdown 2023-09-24 23:53:31 +05:30
Anoop M D
18f185d37c chore: fixed env table styling issue 2023-09-24 23:31:48 +05:30
Anoop M D
7a0322d09e Merge pull request #209 from usebruno/feature/env-secrets
Feature/env secrets
2023-09-24 23:22:40 +05:30
Anoop M D
2dadad3af0 Merge branch 'main' into feature/env-secrets 2023-09-24 23:11:45 +05:30
Anoop M D
eaa31342dc Merge pull request #207 from mirkogolze/feature/env-secrets
#199 improve code to check given env vars correctly
2023-09-24 23:07:08 +05:30
Anoop M D
c4fd9d38a5 Merge pull request #208 from mirkogolze/feature/cli-env-vars
#199 bring feature cli overridable env vars to main
2023-09-24 23:05:19 +05:30
Anoop M D
9c4c219b99 feat(#199): Env Secrets - UI and Electron Layer updates 2023-09-24 23:02:39 +05:30
Mirko Golze
8e22aa2fca #199 improve code to check given envvars correctly 2023-09-24 15:40:04 +02:00
Mirko Golze
6b9e085696 #199 small code refactoring 2023-09-24 15:39:56 +02:00
Mirko Golze
74282706aa #199 add CLI feature to use command line parameter '--env-var secret=xzy123' 2023-09-24 15:39:51 +02:00
Mirko Golze
aa88aa73a2 #199 improve code to check given envvars correctly 2023-09-24 15:28:33 +02:00
Anoop M D
f78c1640e9 feat(#199): electron safeStorage util for storing secrets with aes256 fallback 2023-09-24 17:49:28 +05:30
Anoop M D
a5a17cf8eb fix(#131): fixed macos ctrl+a select all issue 2023-09-24 02:47:05 +05:30
Anoop M D
c5a86cb343 feat: documentation link in app titlebar 2023-09-24 02:33:58 +05:30
Anoop M D
9b94cddc9b Merge pull request #204 from jeffprinty/enhancement/add-about-window
Issue #203 Add about-window to help menu
2023-09-24 02:10:23 +05:30
Anoop M D
0a172ddce8 feat(#206): Collection and Env variables viewer 2023-09-24 02:07:31 +05:30
Jeff Printy
aea1cbba9e Issue #203 Add about-window to help menu 2023-09-22 23:17:53 -05:00
Anoop M D
7a1b44858d Merge pull request #202 from mirkogolze/feature/env-secrets
#199 add CLI feature to use command line parameter '--env-var' secret=…
2023-09-23 03:18:24 +05:30
Anoop M D
1c89ab3450 fix: fixed issue where vm2 instantiated objects were not being sent to renderer process 2023-09-23 03:14:27 +05:30
Anoop M D
e3ce420216 feat(#122): supporting process.env vars in UI and electron layer 2023-09-23 02:55:54 +05:30
Anoop M D
c91fef2264 chore: refactor electron storage related modules 2023-09-22 20:38:45 +05:30
Mirko Golze
c83fce16dc #199 small code refactoring 2023-09-22 09:22:46 +02:00
Mirko Golze
5415e20d7e #199 add CLI feature to use command line parameter '--env-var secret=xzy123' 2023-09-21 22:17:46 +02:00
Anoop M D
2f45b95930 feat(#199): bru lang updates to store environment secrets 2023-09-22 01:08:35 +05:30
Anoop M D
4531cfc994 chore(#197): ran prettier on tests folder 2023-09-22 00:45:42 +05:30
Anoop M D
bd0738198c chore(#197): ran prettier on packages/bruno-testbench 2023-09-22 00:44:47 +05:30
Anoop M D
9a81793151 chore(#197): ran prettier on packages/bruno-tauri 2023-09-22 00:44:28 +05:30
Anoop M D
88c16fa388 chore(#197): ran prettier on packages/bruno-schema 2023-09-22 00:43:56 +05:30
Anoop M D
f68eacfe0d chore(#197): ran prettier on packages/bruno-query 2023-09-22 00:43:31 +05:30
Anoop M D
116e050987 chore(#197): ran prettier on packages/bruno-lang 2023-09-22 00:42:48 +05:30
Anoop M D
5af2f68252 chore(#197): ran prettier on packages/bruno-js 2023-09-22 00:42:14 +05:30
Anoop M D
a53dd76854 chore(#197): ran prettier on packages/bruno-graphql-docs 2023-09-22 00:37:51 +05:30
Anoop M D
67fe264494 chore(#197): ran prettier on packages/bruno-electron 2023-09-22 00:36:42 +05:30
Anoop M D
ae692dde06 chore(#197): ran prettier on packages/bruno-cli 2023-09-22 00:34:11 +05:30
Anoop M D
1c4c5cc0c0 feat(#122): Support parsing of dotenv files 2023-09-22 00:27:27 +05:30
Anoop M D
19ca1af71e chore: release 0.14.1 2023-09-20 13:11:16 +05:30
Anoop M D
4016a83626 Merge pull request #201 from mirkogolze/bugfix/import-postman-header-check
#192 implement fallback to search body language by header 'content-type'
2023-09-20 13:04:25 +05:30
Mirko Golze
71b18c8b21 implement fallback to search body language by content-type header 2023-09-20 08:31:23 +02:00
Anoop M D
b53fcbb3d1 Merge pull request #198 from usebruno/feature/prettier-formatting
feat(#197): prettier formatting on all files in packages/bruno-app
2023-09-18 13:47:05 +05:30
Anoop M D
19a7f397bb feat(#197): prettier formatting on all files in packages/bruno-app 2023-09-18 13:37:00 +05:30
Anoop M D
a103f41d85 Merge pull request #196 from mirkogolze/main
check response type for ResponsePane CodeEditor
2023-09-18 13:20:16 +05:30
Mirko Golze
7d4d1573af set content-type header only if not set by user 2023-09-18 07:12:18 +02:00
Mirko Golze
6f2bb55ecf format change 2023-09-16 22:32:35 +02:00
Mirko Golze
f189cb1a2e check response type to show XML not surrounded by quotation marks and with highlighting 2023-09-16 22:24:35 +02:00
Anoop M D
c020cd94a8 chore: updated @bruno/js version in electron 2023-09-15 17:15:15 +05:30
Anoop M D
0d4f7e6a06 chore: bumped release version to v0.14.0 2023-09-14 00:25:04 +05:30
Anoop M D
f9ed68843d feat(#190): Console Logs in scripts should be written into developer console 2023-09-14 00:20:04 +05:30
Anoop M D
d3a56fdc82 fix(#151, #188): fixed issue where collections vars in tests were not being updated 2023-09-13 23:58:16 +05:30
Anoop M D
e6c3a5af4c feat: refactored run single request events 2023-09-13 23:37:21 +05:30
Anoop M D
cee8073bb7 Merge branch 'main' of github.com:usebruno/bruno 2023-09-13 18:10:22 +05:30
Anoop M D
69be52cf9e chore: electron notarization for mac 2023-09-13 18:09:59 +05:30
Anoop M D
9e400085e3 Merge pull request #187 from BrentShikoski/feature/support-custom-cacert
support custom cacert file
2023-09-09 13:59:05 +05:30
Brent Shikoski
b07bb67943 consistent formatting 2023-09-08 21:33:09 -05:00
Brent Shikoski
593210456a refactor code around https.Agent for flexibility 2023-09-08 21:23:27 -05:00
Brent Shikoski
e328a4615e cacert support through process environment vars 2023-09-08 20:56:12 -05:00
Brent Shikoski
18afb73238 support custom cacerts in electron app 2023-09-08 20:53:18 -05:00
Anoop M D
73b71e0829 release(#183): bruno cli v0.7.1 2023-09-07 12:31:21 +05:30
Anoop M D
4c25aa99aa Merge pull request #183 from BrentShikoski/feature/support-custom-cacert
support alternative cacert file in cli
2023-09-07 12:06:13 +05:30
Anoop M D
23843b5d0a chore: temporarily disabling mac notarization 2023-09-06 22:30:45 +05:30
Anoop M D
7fbd338fa6 feat: bru.getEnvName() 2023-09-06 20:36:55 +05:30
Anoop M D
4aeb5cf56d Merge pull request #182 from jeffprinty/feature/sidebar-collection-scrolling
Enable scrolling in collections sidebar #118
2023-09-05 12:42:36 +05:30
Brent Shikoski
1ed39a5ea6 support alternative cacert 2023-09-04 15:49:36 -05:00
Jeff Printy
74f248782b Enable scrolling in collections sidebar #118 2023-09-04 14:21:59 -05:00
Anoop M D
99239e19b4 release bruno cli v0.7.0 2023-09-04 12:24:18 +05:30
Anoop M D
f46160e161 Merge pull request #181 from BrentShikoski/feature/cli-certificate-validation
add ability to turn certificate validation off in the cli
2023-09-04 12:09:28 +05:30
Brent Shikoski
d0147778db add ability to turn certificate validation off 2023-09-03 20:43:32 -05:00
Brent Shikoski
87119cee2e add ability to turn certificate validation off 2023-09-03 20:19:56 -05:00
Anoop M D
b25d896dd6 fix(#178): fixed await issue while running scripts 2023-09-01 17:49:34 +05:30
Anoop M D
08495e7fb5 fix(#178): fixed issue in bruno cli where collection run was getting aborted for 4xx/5xx responses 2023-09-01 14:18:41 +05:30
Anoop M D
ee084696f5 chore: release v0.13.2 2023-09-01 14:17:29 +05:30
Anoop M D
aeb9fc8875 chore: release bruno packages to npm 2023-09-01 13:43:26 +05:30
Anoop M D
26a05f92cb chore: bumped version v0.13.1 2023-09-01 00:15:14 +05:30
Anoop M D
fff7819c46 fix(#177): fixed issue where collection run was getting aborted after 4xx/5xx response 2023-09-01 00:13:31 +05:30
Anoop M D
1d678ee0d9 chore: bump version to 0.13.0 2023-08-31 23:03:34 +05:30
Anoop M D
fd4c188c95 feat(#177 #165): running tests and assertions for error responses 2023-08-30 21:35:59 +05:30
Anoop M D
97678b05fc Merge branch 'main' of github.com:usebruno/bruno 2023-08-30 20:31:57 +05:30
Anoop M D
a1c9625aee feat(#168): disable ssl option 2023-08-30 20:31:15 +05:30
Anoop M D
a9d74467ff Merge pull request #176 from LesageYann/chore/doc-improve-suggest
chore: improve the cli doc to avoid misunderstandings
2023-08-29 20:05:55 +05:30
Lesage Yann
bd670eceb6 chore: improve the cli doc to avoid misunderstandings on the command: 2023-08-29 11:36:35 +02:00
Anoop M D
1ccb66e92a Merge pull request #173 from dcoomber/feature/48-husky-prettier
Added prettier precommit hook with husky Re #48
2023-08-29 10:47:09 +05:30
Anoop M D
d62881fe0d feat: updated electron builder config to use js based config 2023-08-28 23:14:40 +05:30
David Coomber
9ea95b4571 Copy prettier config from packages/bruno-app Re #48 2023-08-19 10:40:54 +02:00
David Coomber
df9322b767 Added prettier precommit hook with husky Re #48 2023-08-19 09:20:46 +02:00
Anoop M D
c27c750c3e feat: preferences local storage and electron sync 2023-08-19 00:36:37 +05:30
Anoop M D
94baee8e25 feat: Preferences (General Tab) 2023-08-18 01:09:00 +05:30
Anoop M D
417b50b0ad feat: Preferences (Theme and Support) 2023-08-18 00:18:30 +05:30
Anoop M D
bf5ee7e409 Merge pull request #171 from dcoomber/feature/process-env-secrets
RFC: Support process.env in Bru script Re #170
2023-08-17 23:00:41 +05:30
David Coomber
3ca0107f1b Proposal to support process.env in Bru script Re #170 2023-08-11 19:48:09 +02:00
Paul Edwards
aeb29393c5 Code Editor Mode and formatting 2023-07-19 20:06:37 -04:00
Anoop M D
ec22fdb637 chore: initial commit for bruno docs package 2023-05-19 19:57:58 +05:30
Paul Edwards
0866d33858 Merge branch 'main' of https://github.com/usebruno/bruno into main 2023-05-17 00:09:23 -04:00
Anoop M D
66024d04e9 chore: version bump 2023-05-02 15:26:27 +05:30
Anoop M D
330a7ad18a fix: fixed bug where content type was getting overwritten everytime as application/x-www-form-urlencoded 2023-05-02 15:25:07 +05:30
Anoop M D
2976842588 chore: version bump for bruno cli v0.5.0 2023-04-30 12:10:08 +05:30
Anoop M D
51e0ea2c2d feat(#155): exit process with non zero code when tests or assertions fail 2023-04-30 12:08:23 +05:30
Anoop M D
3b85e7ebcc chore: version bump v0.12.1 2023-04-27 19:26:41 +05:30
Anoop M D
49b0f3a322 fix(#148) : fixed issue where form url encoded params were not being interpolated 2023-04-27 19:25:52 +05:30
Paul Edwards
ad905d1a0a XML Indenting with header check 2023-04-26 22:06:52 -04:00
Anoop M D
45ca5ded96 chore: bumped version to v0.12.0 2023-04-20 11:54:36 +05:30
Anoop M D
b6528062f0 fix: fixed issue where postman collection import was failing when the filename had / or ? chars (#147) 2023-04-20 11:46:55 +05:30
Anoop M D
86094cc054 fix: fixed issue where cancelling requests was throwing an error (#146) 2023-04-20 11:19:12 +05:30
Anoop M D
8e0bc68ada feat: support node-fetch as an inbuilt library (#138) 2023-04-20 10:51:29 +05:30
Anoop M D
c36c7b44a6 Merge pull request #145 from DivyMohan14/feature/async-pre-request-scripts
Adding support for using async pre-request scripts
2023-04-20 09:55:06 +05:30
Divy Mohan Rai
0ac27dee56 feature(async-script): adding support for using async pre-request scripts 2023-04-15 12:33:45 +05:30
Anoop M D
ede122ab09 fix: fix image rendering issues in bru cli npm readme 2023-04-01 13:57:59 +05:30
Anoop M D
96e368cb18 fix: fixed bru cli typos 2023-04-01 13:55:32 +05:30
Anoop M D
942b75861c fix: fixed broken link in bruno cli readme 2023-04-01 13:54:33 +05:30
Anoop M D
c1711ea01b fix: fixing npm image rendering issues in readme 2023-04-01 13:53:32 +05:30
Anoop M D
dedfefbc9a chore: release cli docs to npm 2023-04-01 13:48:35 +05:30
Anoop M D
65dd5df87e chore: added screenshot of bru cli output 2023-04-01 13:46:13 +05:30
Anoop M D
78d2393686 chore: prep for bruno-cli release 2023-04-01 13:40:23 +05:30
Anoop M D
0b65c4580e chore: updated image 2023-03-30 11:04:20 +05:30
Anoop M D
9cc1bf1e2f chore: updated readme 2023-03-30 10:56:37 +05:30
Anoop M D
b96e3d0f23 release: v0.11.0 2023-03-29 13:00:03 +05:30
Anoop M D
11c99d55dc fix: fixed yml indentation issue 2023-03-29 12:58:03 +05:30
Anoop M D
d054dc4c78 feat(#105): github workflow for release updates to homebrew 2023-03-29 12:55:49 +05:30
Anoop M D
9014dc5769 fix: fixed dark mode styling issue in assertions op selector 2023-03-29 12:49:17 +05:30
Anoop M D
d346970241 fix: fixed issue where unsaved changes where not being picked up while running tests (#125) 2023-03-29 12:43:47 +05:30
Anoop M D
bdb3051c2b fix: fixed issue where env vars was not getting embedded inside xml req body (#141) 2023-03-29 12:22:42 +05:30
Anoop M D
f8325b22b3 fix: fixed issue where env vars was not getting embedded inside xml req body (#141) 2023-03-29 12:20:40 +05:30
346 changed files with 41557 additions and 3963 deletions

View File

@@ -0,0 +1,12 @@
name: Bump Homebrew Cask
on:
release:
types: [published]
jobs:
bump:
runs-on: macos-10.15
steps:
- name: Bump Homebrew Cask
run: brew bump-cask-pr bruno --version "${GITHUB_REF_NAME#v}"

View File

@@ -1,29 +1,35 @@
name: Unit Tests
on:
push:
branches: [ main ]
branches: [main]
pull_request:
branches: [ main ]
branches: [main]
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Install dependencies
run: npm i --legacy-peer-deps
- name: Test Package bruno-query
run: npm run test --workspace=packages/bruno-query
- name: Build Package bruno-query
run: npm run build --workspace=packages/bruno-query
- name: Test Package bruno-lang
run: npm run test --workspace=packages/bruno-lang
- name: Test Package bruno-schema
run: npm run test --workspace=packages/bruno-schema
- name: Test Package bruno-app
run: npm run test --workspace=packages/bruno-app
- name: Test Package bruno-js
run: npm run test --workspace=packages/bruno-js
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Check package-lock.json
run: npm ci
- name: Install dependencies
run: npm i --legacy-peer-deps
- name: Test Package bruno-query
run: npm run test --workspace=packages/bruno-query
- name: Build Package bruno-query
run: npm run build --workspace=packages/bruno-query
- name: Test Package bruno-lang
run: npm run test --workspace=packages/bruno-lang
- name: Test Package bruno-schema
run: npm run test --workspace=packages/bruno-schema
- name: Test Package bruno-app
run: npm run test --workspace=packages/bruno-app
- name: Test Package bruno-js
run: npm run test --workspace=packages/bruno-js
- name: Test Package bruno-cli
run: npm run test --workspace=packages/bruno-cli
- name: Test Package bruno-electron
run: npm run test --workspace=packages/bruno-electron

5
.gitignore vendored
View File

@@ -4,7 +4,6 @@
node_modules
yarn.lock
pnpm-lock.yaml
package-lock.json
.pnp
.pnp.js
@@ -41,3 +40,7 @@ yarn-error.log*
/test-results/
/playwright-report/
/playwright/.cache/
#dev editor
bruno.iml
.idea

4
.husky/pre-commit Executable file
View File

@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx pretty-quick --staged

7
.prettierrc.json Normal file
View File

@@ -0,0 +1,7 @@
{
"trailingComma": "none",
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"printWidth": 120
}

BIN
assets/images/cli-demo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

View File

@@ -1,3 +1,5 @@
**English** | [Русский](/contributing_ru.md)
## Lets make bruno better, together !!
I am happy that you are looking to improve bruno. Below are the guidelines to get started bringing up bruno on your computer.
@@ -19,7 +21,7 @@ Libraries we use
### Dependencies
You would need [Node v14.x or the latest LTS version](https://nodejs.org/en/) and npm 8.x. We use npm workspaces in the project
You would need [Node v18.x or the latest LTS version](https://nodejs.org/en/) and npm 8.x. We use npm workspaces in the project
### Lets start coding

37
contributing_ru.md Normal file
View File

@@ -0,0 +1,37 @@
[English](/contributing.md) | **Русский**
## Давайте вместе сделаем Бруно лучше!!!
Я рад, что Вы хотите усовершенствовать 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
### Приступим к коду
Пожалуйста, обратитесь к [development_ru.md](docs/development_ru.md) для получения инструкций по запуску локальной среды разработки.
### Создание Pull Request
- Пожалуйста, пусть PR будет небольшим и сфокусированным на одной вещи
- Пожалуйста, соблюдайте формат создания веток
- feature/[название функции]: Эта ветка должна содержать изменения для конкретной функции
- Пример: feature/dark-mode
- bugfix/[название ошибки]: Эта ветка должна содержать только исправления для конкретной ошибки
- Пример bugfix/bug-1

View File

@@ -1,9 +1,12 @@
**English** | [Русский](/docs/development_ru.md)
## Development
Bruno is deing developed as a desktop app. You need to load the app by running the nextjs app in one terminal and then run the electron app in another terminal.
Bruno is being developed as a desktop app. You need to load the app by running the nextjs app in one terminal and then run the electron app in another terminal.
### Dependencies
* NodeJS v18
- NodeJS v18
### Local Development
@@ -15,7 +18,6 @@ nvm use
npm i --legacy-peer-deps
# build graphql docs
# note: you can for now ignore the error thrown while building the graphql docs
npm run build:graphql-docs
# build bruno query

55
docs/development_ru.md Normal file
View File

@@ -0,0 +1,55 @@
[English](/docs/development.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
# запустить приложение electron ( терминал 2 )
npm run dev:electron
```
### Устранение неисправностей
При запуске `npm install` может возникнуть ошибка `Unsupported platform`. Чтобы исправить это, необходимо удалить `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
```

29802
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -13,11 +13,14 @@
"packages/bruno-testbench",
"packages/bruno-graphql-docs"
],
"homepage": "https://usebruno.com",
"devDependencies": {
"@faker-js/faker": "^7.6.0",
"@jest/globals": "^29.2.0",
"@playwright/test": "^1.27.1",
"husky": "^8.0.3",
"jest": "^29.2.0",
"pretty-quick": "^3.1.3",
"randomstring": "^1.2.2",
"ts-jest": "^29.0.5"
},
@@ -30,9 +33,10 @@
"build:graphql-docs": "npm run build --workspace=packages/bruno-graphql-docs",
"build:electron": "./scripts/build-electron.sh",
"test:e2e": "npx playwright test",
"test:report": "npx playwright show-report"
"test:report": "npx playwright show-report",
"prepare": "husky install"
},
"overrides": {
"rollup": "3.2.5"
}
}
}

View File

@@ -3,5 +3,5 @@
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"printWidth": 180
"printWidth": 120
}

View File

@@ -18,7 +18,7 @@
"@tabler/icons": "^1.46.0",
"@tippyjs/react": "^4.2.6",
"@usebruno/graphql-docs": "0.1.0",
"@usebruno/schema": "0.3.1",
"@usebruno/schema": "0.5.0",
"axios": "^0.26.0",
"classnames": "^2.3.1",
"codemirror": "^5.65.2",
@@ -30,8 +30,11 @@
"graphiql": "^1.5.9",
"graphql": "^16.6.0",
"graphql-request": "^3.7.0",
"handlebars": "^4.7.8",
"httpsnippet": "^3.0.1",
"idb": "^7.0.0",
"immer": "^9.0.15",
"know-your-http-well": "^0.5.0",
"lodash": "^4.17.21",
"markdown-it": "^13.0.1",
"mousetrap": "^1.6.5",
@@ -47,11 +50,13 @@
"react-dom": "18.2.0",
"react-github-btn": "^1.4.0",
"react-hot-toast": "^2.4.0",
"react-inspector": "^6.0.2",
"react-redux": "^7.2.6",
"react-tooltip": "^5.5.2",
"sass": "^1.46.0",
"styled-components": "^5.3.3",
"tailwindcss": "^2.2.19",
"xml-formatter": "^3.5.0",
"yup": "^0.32.11"
},
"devDependencies": {

View File

@@ -14,7 +14,11 @@ const Bruno = ({ width }) => {
stroke="none"
points="36,47.2521 32.9167,49.6688 30.4167,49.6688 30.3333,53.5021 31.0833,57.0021 32.1667,58.9188 35,60.4188 39.5833,59.8355 41.1667,58.0855 42.1667,53.8355 41.9167,49.8355 39.9167,50.0855"
/>
<polygon fill="#3F3F3F" stroke="none" points="32.5,36.9188 30.9167,40.6688 33.0833,41.9188 34.3333,42.4188 38.6667,42.5855 41.5833,40.3355 39.8333,37.0855" />
<polygon
fill="#3F3F3F"
stroke="none"
points="32.5,36.9188 30.9167,40.6688 33.0833,41.9188 34.3333,42.4188 38.6667,42.5855 41.5833,40.3355 39.8333,37.0855"
/>
</g>
<g id="hair" />
<g id="skin" />
@@ -84,8 +88,27 @@ const Bruno = ({ width }) => {
strokeWidth="2"
d="M52.6309,46.4628c0,0-3.0781,6.7216-7.8049,8.2712"
/>
<path fill="none" stroke="#000000" strokeLinecap="round" strokeLinejoin="round" strokeMiterlimit="10" strokeWidth="2" d="M19.437,46.969c0,0,3.0781,6.0823,7.8049,7.632" />
<line x1="36.2078" x2="36.2078" y1="47.3393" y2="44.3093" fill="none" stroke="#000000" strokeLinecap="round" strokeLinejoin="round" strokeMiterlimit="10" strokeWidth="2" />
<path
fill="none"
stroke="#000000"
strokeLinecap="round"
strokeLinejoin="round"
strokeMiterlimit="10"
strokeWidth="2"
d="M19.437,46.969c0,0,3.0781,6.0823,7.8049,7.632"
/>
<line
x1="36.2078"
x2="36.2078"
y1="47.3393"
y2="44.3093"
fill="none"
stroke="#000000"
strokeLinecap="round"
strokeLinejoin="round"
strokeMiterlimit="10"
strokeWidth="2"
/>
</g>
</svg>
);

View File

@@ -29,7 +29,7 @@ const BrunoSupport = ({ onClose }) => {
<div className="mt-2">
<a href="https://github.com/usebruno/bruno" target="_blank" className="flex items-end">
<IconBrandGithub size={18} strokeWidth={2} />
<span className="label ml-2">Github</span>
<span className="label ml-2">GitHub</span>
</a>
</div>
<div className="mt-2">

View File

@@ -6,7 +6,8 @@ const StyledWrapper = styled.div`
border: solid 1px ${(props) => props.theme.codemirror.border};
}
.CodeMirror-overlayscroll-horizontal div, .CodeMirror-overlayscroll-vertical div {
.CodeMirror-overlayscroll-horizontal div,
.CodeMirror-overlayscroll-vertical div {
background: #d2d7db;
}
@@ -17,12 +18,14 @@ const StyledWrapper = styled.div`
// Todo: dark mode temporary fix
// Clean this
.CodeMirror.cm-s-monokai {
.CodeMirror-overlayscroll-horizontal div, .CodeMirror-overlayscroll-vertical div {
.CodeMirror-overlayscroll-horizontal div,
.CodeMirror-overlayscroll-vertical div {
background: #444444;
}
}
.cm-s-monokai span.cm-property, .cm-s-monokai span.cm-attribute {
.cm-s-monokai span.cm-property,
.cm-s-monokai span.cm-attribute {
color: #9cdcfe !important;
}
@@ -30,16 +33,20 @@ const StyledWrapper = styled.div`
color: #ce9178 !important;
}
.cm-s-monokai span.cm-number{
.cm-s-monokai span.cm-number {
color: #b5cea8 !important;
}
.cm-s-monokai span.cm-atom{
.cm-s-monokai span.cm-atom {
color: #569cd6 !important;
}
.cm-variable-valid{color: green}
.cm-variable-invalid{color: red}
.cm-variable-valid {
color: green;
}
.cm-variable-invalid {
color: red;
}
`;
export default StyledWrapper;

View File

@@ -43,7 +43,7 @@ export default class CodeEditor extends React.Component {
foldGutter: true,
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
readOnly: this.props.readOnly,
scrollbarStyle: "overlay",
scrollbarStyle: 'overlay',
theme: this.props.theme === 'dark' ? 'monokai' : 'default',
extraKeys: {
'Cmd-Enter': () => {
@@ -80,7 +80,7 @@ export default class CodeEditor extends React.Component {
}
componentDidUpdate(prevProps) {
// Ensure the changes caused by this update are not interpretted as
// Ensure the changes caused by this update are not interpreted as
// user-input changes which could otherwise result in an infinite
// event loop.
this.ignoreChangeEvent = true;
@@ -96,7 +96,7 @@ export default class CodeEditor extends React.Component {
this.editor.setValue(this.props.value);
}
if(this.editor) {
if (this.editor) {
let variables = getEnvironmentVariables(this.props.collection);
if (!isEqual(variables, this.variables)) {
this.addOverlay();
@@ -119,7 +119,7 @@ export default class CodeEditor extends React.Component {
render() {
return (
<StyledWrapper
className="h-full"
className="h-full w-full"
aria-label="Code Editor"
ref={(node) => {
this._node = node;
@@ -135,7 +135,7 @@ export default class CodeEditor extends React.Component {
defineCodeMirrorBrunoVariablesMode(variables, mode);
this.editor.setOption('mode', 'brunovariables');
}
};
_onEdit = () => {
if (!this.ignoreChangeEvent && this.editor) {

View File

@@ -0,0 +1,28 @@
import styled from 'styled-components';
const Wrapper = styled.div`
font-size: 0.8125rem;
.auth-mode-selector {
background: transparent;
.auth-mode-label {
color: ${(props) => props.theme.colors.text.yellow};
}
.dropdown-item {
padding: 0.2rem 0.6rem !important;
}
.label-item {
padding: 0.2rem 0.6rem !important;
}
}
.caret {
color: rgb(140, 140, 140);
fill: rgb(140 140 140);
}
`;
export default Wrapper;

View File

@@ -0,0 +1,69 @@
import React, { useRef, forwardRef } from 'react';
import get from 'lodash/get';
import { IconCaretDown } from '@tabler/icons';
import Dropdown from 'components/Dropdown';
import { useDispatch } from 'react-redux';
import { updateCollectionAuthMode } from 'providers/ReduxStore/slices/collections';
import { humanizeRequestAuthMode } from 'utils/collections';
import StyledWrapper from './StyledWrapper';
const AuthMode = ({ collection }) => {
const dispatch = useDispatch();
const dropdownTippyRef = useRef();
const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref);
const authMode = get(collection, 'root.request.auth.mode');
const Icon = forwardRef((props, ref) => {
return (
<div ref={ref} className="flex items-center justify-center auth-mode-label select-none">
{humanizeRequestAuthMode(authMode)} <IconCaretDown className="caret ml-1 mr-1" size={14} strokeWidth={2} />
</div>
);
});
const onModeChange = (value) => {
dispatch(
updateCollectionAuthMode({
collectionUid: collection.uid,
mode: value
})
);
};
return (
<StyledWrapper>
<div className="inline-flex items-center cursor-pointer auth-mode-selector">
<Dropdown onCreate={onDropdownCreate} icon={<Icon />} placement="bottom-end">
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
onModeChange('basic');
}}
>
Basic Auth
</div>
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
onModeChange('bearer');
}}
>
Bearer Token
</div>
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
onModeChange('none');
}}
>
No Auth
</div>
</Dropdown>
</div>
</StyledWrapper>
);
};
export default AuthMode;

View File

@@ -0,0 +1,16 @@
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};
}
`;
export default Wrapper;

View File

@@ -0,0 +1,71 @@
import React from 'react';
import get from 'lodash/get';
import { useTheme } from 'providers/Theme';
import { useDispatch } from 'react-redux';
import SingleLineEditor from 'components/SingleLineEditor';
import { updateCollectionAuth } from 'providers/ReduxStore/slices/collections';
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
import StyledWrapper from './StyledWrapper';
const BasicAuth = ({ collection }) => {
const dispatch = useDispatch();
const { storedTheme } = useTheme();
const basicAuth = get(collection, 'root.request.auth.basic', {});
const handleSave = () => dispatch(saveCollectionRoot(collection.uid));
const handleUsernameChange = (username) => {
dispatch(
updateCollectionAuth({
mode: 'basic',
collectionUid: collection.uid,
content: {
username: username,
password: basicAuth.password
}
})
);
};
const handlePasswordChange = (password) => {
dispatch(
updateCollectionAuth({
mode: 'basic',
collectionUid: collection.uid,
content: {
username: basicAuth.username,
password: password
}
})
);
};
return (
<StyledWrapper className="mt-2 w-full">
<label className="block font-medium mb-2">Username</label>
<div className="single-line-editor-wrapper mb-2">
<SingleLineEditor
value={basicAuth.username || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleUsernameChange(val)}
collection={collection}
/>
</div>
<label className="block font-medium mb-2">Password</label>
<div className="single-line-editor-wrapper">
<SingleLineEditor
value={basicAuth.password || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handlePasswordChange(val)}
collection={collection}
/>
</div>
</StyledWrapper>
);
};
export default BasicAuth;

View File

@@ -0,0 +1,16 @@
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};
}
`;
export default Wrapper;

View File

@@ -0,0 +1,46 @@
import React from 'react';
import get from 'lodash/get';
import { useTheme } from 'providers/Theme';
import { useDispatch } from 'react-redux';
import SingleLineEditor from 'components/SingleLineEditor';
import { updateCollectionAuth } from 'providers/ReduxStore/slices/collections';
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
import StyledWrapper from './StyledWrapper';
const BearerAuth = ({ collection }) => {
const dispatch = useDispatch();
const { storedTheme } = useTheme();
const bearerToken = get(collection, 'root.request.auth.bearer.token');
const handleSave = () => dispatch(saveCollectionRoot(collection.uid));
const handleTokenChange = (token) => {
dispatch(
updateCollectionAuth({
mode: 'bearer',
collectionUid: collection.uid,
content: {
token: token
}
})
);
};
return (
<StyledWrapper className="mt-2 w-full">
<label className="block font-medium mb-2">Token</label>
<div className="single-line-editor-wrapper">
<SingleLineEditor
value={bearerToken}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleTokenChange(val)}
collection={collection}
/>
</div>
</StyledWrapper>
);
};
export default BearerAuth;

View File

@@ -0,0 +1,5 @@
import styled from 'styled-components';
const Wrapper = styled.div``;
export default Wrapper;

View File

@@ -0,0 +1,42 @@
import React from 'react';
import get from 'lodash/get';
import { useDispatch } from 'react-redux';
import AuthMode from './AuthMode';
import BearerAuth from './BearerAuth';
import BasicAuth from './BasicAuth';
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
import StyledWrapper from './StyledWrapper';
const Auth = ({ collection }) => {
const authMode = get(collection, 'root.request.auth.mode');
const dispatch = useDispatch();
const handleSave = () => dispatch(saveCollectionRoot(collection.uid));
const getAuthView = () => {
switch (authMode) {
case 'basic': {
return <BasicAuth collection={collection} />;
}
case 'bearer': {
return <BearerAuth collection={collection} />;
}
}
};
return (
<StyledWrapper className="w-full mt-2">
<div className="flex flex-grow justify-start items-center">
<AuthMode collection={collection} />
</div>
{getAuthView()}
<div className="mt-6">
<button type="submit" className="submit btn btn-sm btn-secondary" onClick={handleSave}>
Save
</button>
</div>
</StyledWrapper>
);
};
export default Auth;

View File

@@ -0,0 +1,56 @@
import styled from 'styled-components';
const Wrapper = styled.div`
table {
width: 100%;
border-collapse: collapse;
font-weight: 600;
table-layout: fixed;
thead,
td {
border: 1px solid ${(props) => props.theme.table.border};
}
thead {
color: ${(props) => props.theme.table.thead.color};
font-size: 0.8125rem;
user-select: none;
}
td {
padding: 6px 10px;
&:nth-child(1) {
width: 30%;
}
&:nth-child(3) {
width: 70px;
}
}
}
.btn-add-header {
font-size: 0.8125rem;
}
input[type='text'] {
width: 100%;
border: solid 1px transparent;
outline: none !important;
background-color: inherit;
&:focus {
outline: none !important;
border: solid 1px transparent;
}
}
input[type='checkbox'] {
cursor: pointer;
position: relative;
top: 1px;
}
`;
export default Wrapper;

View File

@@ -0,0 +1,151 @@
import React from 'react';
import get from 'lodash/get';
import cloneDeep from 'lodash/cloneDeep';
import { IconTrash } from '@tabler/icons';
import { useDispatch } from 'react-redux';
import { useTheme } from 'providers/Theme';
import {
addCollectionHeader,
updateCollectionHeader,
deleteCollectionHeader
} from 'providers/ReduxStore/slices/collections';
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
import SingleLineEditor from 'components/SingleLineEditor';
import StyledWrapper from './StyledWrapper';
import { headers as StandardHTTPHeaders } from 'know-your-http-well';
const headerAutoCompleteList = StandardHTTPHeaders.map((e) => e.header);
const Headers = ({ collection }) => {
const dispatch = useDispatch();
const { storedTheme } = useTheme();
const headers = get(collection, 'root.request.headers', []);
const addHeader = () => {
dispatch(
addCollectionHeader({
collectionUid: collection.uid
})
);
};
const handleSave = () => dispatch(saveCollectionRoot(collection.uid));
const handleHeaderValueChange = (e, _header, type) => {
const header = cloneDeep(_header);
switch (type) {
case 'name': {
header.name = e.target.value;
break;
}
case 'value': {
header.value = e.target.value;
break;
}
case 'enabled': {
header.enabled = e.target.checked;
break;
}
}
dispatch(
updateCollectionHeader({
header: header,
collectionUid: collection.uid
})
);
};
const handleRemoveHeader = (header) => {
dispatch(
deleteCollectionHeader({
headerUid: header.uid,
collectionUid: collection.uid
})
);
};
return (
<StyledWrapper className="w-full">
<table>
<thead>
<tr>
<td>Name</td>
<td>Value</td>
<td></td>
</tr>
</thead>
<tbody>
{headers && headers.length
? headers.map((header) => {
return (
<tr key={header.uid}>
<td>
<SingleLineEditor
value={header.name}
theme={storedTheme}
onSave={handleSave}
onChange={(newValue) =>
handleHeaderValueChange(
{
target: {
value: newValue
}
},
header,
'name'
)
}
autocomplete={headerAutoCompleteList}
collection={collection}
/>
</td>
<td>
<SingleLineEditor
value={header.value}
theme={storedTheme}
onSave={handleSave}
onChange={(newValue) =>
handleHeaderValueChange(
{
target: {
value: newValue
}
},
header,
'value'
)
}
collection={collection}
/>
</td>
<td>
<div className="flex items-center">
<input
type="checkbox"
checked={header.enabled}
tabIndex="-1"
className="mr-3 mousetrap"
onChange={(e) => handleHeaderValueChange(e, header, 'enabled')}
/>
<button tabIndex="-1" onClick={() => handleRemoveHeader(header)}>
<IconTrash strokeWidth={1.5} size={20} />
</button>
</div>
</td>
</tr>
);
})
: null}
</tbody>
</table>
<button className="btn-add-header text-link pr-2 py-3 mt-2 select-none" onClick={addHeader}>
+ Add Header
</button>
<div className="mt-6">
<button type="submit" className="submit btn btn-sm btn-secondary" onClick={handleSave}>
Save
</button>
</div>
</StyledWrapper>
);
};
export default Headers;

View File

@@ -0,0 +1,27 @@
import styled from 'styled-components';
const StyledWrapper = styled.div`
.settings-label {
width: 80px;
}
.textbox {
border: 1px solid #ccc;
padding: 0.15rem 0.45rem;
box-shadow: none;
border-radius: 0px;
outline: none;
box-shadow: none;
transition: border-color ease-in-out 0.1s;
border-radius: 3px;
background-color: ${(props) => props.theme.modal.input.bg};
border: 1px solid ${(props) => props.theme.modal.input.border};
&:focus {
border: solid 1px ${(props) => props.theme.modal.input.focusBorder} !important;
outline: none !important;
}
}
`;
export default StyledWrapper;

View File

@@ -0,0 +1,200 @@
import React, { useEffect } from 'react';
import { useFormik } from 'formik';
import * as Yup from 'yup';
import StyledWrapper from './StyledWrapper';
const ProxySettings = ({ proxyConfig, onUpdate }) => {
const formik = useFormik({
initialValues: {
enabled: proxyConfig.enabled || false,
protocol: proxyConfig.protocol || 'http',
hostname: proxyConfig.hostname || '',
port: proxyConfig.port || '',
auth: {
enabled: proxyConfig.auth ? proxyConfig.auth.enabled || false : false,
username: proxyConfig.auth ? proxyConfig.auth.username || '' : '',
password: proxyConfig.auth ? proxyConfig.auth.password || '' : ''
}
},
validationSchema: Yup.object({
enabled: Yup.boolean(),
protocol: Yup.string().oneOf(['http', 'https', 'socks5']),
hostname: Yup.string().max(1024),
port: Yup.number().min(0).max(65535),
auth: Yup.object({
enabled: Yup.boolean(),
username: Yup.string().max(1024),
password: Yup.string().max(1024)
})
}),
onSubmit: (values) => {
onUpdate(values);
}
});
useEffect(() => {
formik.setValues({
enabled: proxyConfig.enabled || false,
protocol: proxyConfig.protocol || 'http',
hostname: proxyConfig.hostname || '',
port: proxyConfig.port || '',
auth: {
enabled: proxyConfig.auth ? proxyConfig.auth.enabled || false : false,
username: proxyConfig.auth ? proxyConfig.auth.username || '' : '',
password: proxyConfig.auth ? proxyConfig.auth.password || '' : ''
}
});
}, [proxyConfig]);
return (
<StyledWrapper>
<form className="bruno-form" onSubmit={formik.handleSubmit}>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="enabled">
Enabled
</label>
<input type="checkbox" name="enabled" checked={formik.values.enabled} onChange={formik.handleChange} />
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="protocol">
Protocol
</label>
<div className="flex items-center">
<label className="flex items-center">
<input
type="radio"
name="protocol"
value="http"
checked={formik.values.protocol === 'http'}
onChange={formik.handleChange}
className="mr-1"
/>
http
</label>
<label className="flex items-center ml-4">
<input
type="radio"
name="protocol"
value="https"
checked={formik.values.protocol === 'https'}
onChange={formik.handleChange}
className="mr-1"
/>
https
</label>
<label className="flex items-center ml-4">
<input
type="radio"
name="protocol"
value="socks5"
checked={formik.values.protocol === 'socks5'}
onChange={formik.handleChange}
className="mr-1"
/>
socks5
</label>
</div>
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="hostname">
Hostname
</label>
<input
id="hostname"
type="text"
name="hostname"
className="block textbox"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
onChange={formik.handleChange}
value={formik.values.hostname || ''}
/>
{formik.touched.hostname && formik.errors.hostname ? (
<div className="text-red-500">{formik.errors.hostname}</div>
) : null}
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="port">
Port
</label>
<input
id="port"
type="number"
name="port"
className="block textbox"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
onChange={formik.handleChange}
value={formik.values.port}
/>
{formik.touched.port && formik.errors.port ? <div className="text-red-500">{formik.errors.port}</div> : null}
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="auth.enabled">
Auth
</label>
<input
type="checkbox"
name="auth.enabled"
checked={formik.values.auth.enabled}
onChange={formik.handleChange}
/>
</div>
<div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="auth.username">
Username
</label>
<input
id="auth.username"
type="text"
name="auth.username"
className="block textbox"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={formik.values.auth.username}
onChange={formik.handleChange}
/>
{formik.touched.auth?.username && formik.errors.auth?.username ? (
<div className="text-red-500">{formik.errors.auth.username}</div>
) : null}
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="auth.password">
Password
</label>
<input
id="auth.password"
type="text"
name="auth.password"
className="block textbox"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={formik.values.auth.password}
onChange={formik.handleChange}
/>
{formik.touched.auth?.password && formik.errors.auth?.password ? (
<div className="text-red-500">{formik.errors.auth.password}</div>
) : null}
</div>
</div>
<div className="mt-6">
<button type="submit" className="submit btn btn-sm btn-secondary">
Save
</button>
</div>
</form>
</StyledWrapper>
);
};
export default ProxySettings;

View File

@@ -0,0 +1,13 @@
import styled from 'styled-components';
const StyledWrapper = styled.div`
div.CodeMirror {
height: inherit;
}
div.title {
color: var(--color-tab-inactive);
}
`;
export default StyledWrapper;

View File

@@ -0,0 +1,73 @@
import React from 'react';
import get from 'lodash/get';
import { useDispatch } from 'react-redux';
import CodeEditor from 'components/CodeEditor';
import { updateCollectionRequestScript, updateCollectionResponseScript } from 'providers/ReduxStore/slices/collections';
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
import { useTheme } from 'providers/Theme';
import StyledWrapper from './StyledWrapper';
const Script = ({ collection }) => {
const dispatch = useDispatch();
const requestScript = get(collection, 'root.request.script.req', '');
const responseScript = get(collection, 'root.request.script.res', '');
const { storedTheme } = useTheme();
const onRequestScriptEdit = (value) => {
dispatch(
updateCollectionRequestScript({
script: value,
collectionUid: collection.uid
})
);
};
const onResponseScriptEdit = (value) => {
dispatch(
updateCollectionResponseScript({
script: value,
collectionUid: collection.uid
})
);
};
const handleSave = () => {
dispatch(saveCollectionRoot(collection.uid));
};
return (
<StyledWrapper className="w-full flex flex-col">
<div className="flex-1 mt-2">
<div className="mb-1 title text-xs">Pre Request</div>
<CodeEditor
collection={collection}
value={requestScript || ''}
theme={storedTheme}
onEdit={onRequestScriptEdit}
mode="javascript"
onSave={handleSave}
/>
</div>
<div className="flex-1 mt-6">
<div className="mt-1 mb-1 title text-xs">Post Response</div>
<CodeEditor
collection={collection}
value={responseScript || ''}
theme={storedTheme}
onEdit={onResponseScriptEdit}
mode="javascript"
onSave={handleSave}
/>
</div>
<div className="mt-12">
<button type="submit" className="submit btn btn-sm btn-secondary" onClick={handleSave}>
Save
</button>
</div>
</StyledWrapper>
);
};
export default Script;

View File

@@ -0,0 +1,46 @@
import styled from 'styled-components';
const StyledWrapper = styled.div`
max-width: 800px;
div.tabs {
div.tab {
padding: 6px 0px;
border: none;
border-bottom: solid 2px transparent;
margin-right: 1.25rem;
color: var(--color-tab-inactive);
cursor: pointer;
&:focus,
&:active,
&:focus-within,
&:focus-visible,
&:target {
outline: none !important;
box-shadow: none !important;
}
&.active {
color: ${(props) => props.theme.tabs.active.color} !important;
border-bottom: solid 2px ${(props) => props.theme.tabs.active.border} !important;
}
}
}
table {
thead,
td {
border: 1px solid ${(props) => props.theme.table.border};
li {
background-color: ${(props) => props.theme.bg} !important;
}
}
}
.muted {
color: ${(props) => props.theme.colors.text.muted};
}
`;
export default StyledWrapper;

View File

@@ -0,0 +1,5 @@
import styled from 'styled-components';
const StyledWrapper = styled.div``;
export default StyledWrapper;

View File

@@ -0,0 +1,47 @@
import React from 'react';
import get from 'lodash/get';
import { useDispatch } from 'react-redux';
import CodeEditor from 'components/CodeEditor';
import { updateCollectionTests } from 'providers/ReduxStore/slices/collections';
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
import { useTheme } from 'providers/Theme';
import StyledWrapper from './StyledWrapper';
const Tests = ({ collection }) => {
const dispatch = useDispatch();
const tests = get(collection, 'root.request.tests', '');
const { storedTheme } = useTheme();
const onEdit = (value) => {
dispatch(
updateCollectionTests({
tests: value,
collectionUid: collection.uid
})
);
};
const handleSave = () => dispatch(saveCollectionRoot(collection.uid));
return (
<StyledWrapper className="w-full flex flex-col h-full">
<CodeEditor
collection={collection}
value={tests || ''}
theme={storedTheme}
onEdit={onEdit}
mode="javascript"
onSave={handleSave}
/>
<div className="mt-6">
<button type="submit" className="submit btn btn-sm btn-secondary" onClick={handleSave}>
Save
</button>
</div>
</StyledWrapper>
);
};
export default Tests;

View File

@@ -0,0 +1,90 @@
import React from 'react';
import classnames from 'classnames';
import get from 'lodash/get';
import cloneDeep from 'lodash/cloneDeep';
import toast from 'react-hot-toast';
import { updateBrunoConfig } from 'providers/ReduxStore/slices/collections/actions';
import { updateSettingsSelectedTab } from 'providers/ReduxStore/slices/collections';
import { useDispatch } from 'react-redux';
import ProxySettings from './ProxySettings';
import Headers from './Headers';
import Auth from './Auth';
import Script from './Script';
import Test from './Tests';
import StyledWrapper from './StyledWrapper';
const CollectionSettings = ({ collection }) => {
const dispatch = useDispatch();
const tab = collection.settingsSelectedTab;
const setTab = (tab) => {
dispatch(
updateSettingsSelectedTab({
collectionUid: collection.uid,
tab
})
);
};
const proxyConfig = get(collection, 'brunoConfig.proxy', {});
const onProxySettingsUpdate = (config) => {
const brunoConfig = cloneDeep(collection.brunoConfig);
brunoConfig.proxy = config;
dispatch(updateBrunoConfig(brunoConfig, collection.uid))
.then(() => {
toast.success('Collection settings updated successfully');
})
.catch((err) => console.log(err) && toast.error('Failed to update collection settings'));
};
const getTabPanel = (tab) => {
switch (tab) {
case 'headers': {
return <Headers collection={collection} />;
}
case 'auth': {
return <Auth collection={collection} />;
}
case 'script': {
return <Script collection={collection} />;
}
case 'tests': {
return <Test collection={collection} />;
}
case 'proxy': {
return <ProxySettings proxyConfig={proxyConfig} onUpdate={onProxySettingsUpdate} />;
}
}
};
const getTabClassname = (tabName) => {
return classnames(`tab select-none ${tabName}`, {
active: tabName === tab
});
};
return (
<StyledWrapper className="flex flex-col h-full relative px-4 py-4">
<div className="flex flex-wrap items-center tabs" role="tablist">
<div className={getTabClassname('headers')} role="tab" onClick={() => setTab('headers')}>
Headers
</div>
<div className={getTabClassname('auth')} role="tab" onClick={() => setTab('auth')}>
Auth
</div>
<div className={getTabClassname('script')} role="tab" onClick={() => setTab('script')}>
Script
</div>
<div className={getTabClassname('tests')} role="tab" onClick={() => setTab('tests')}>
Tests
</div>
<div className={getTabClassname('proxy')} role="tab" onClick={() => setTab('proxy')}>
Proxy
</div>
</div>
<section className={`flex ${['auth', 'script'].includes(tab) ? '' : 'mt-4'}`}>{getTabPanel(tab)}</section>
</StyledWrapper>
);
};
export default CollectionSettings;

View File

@@ -43,7 +43,7 @@ const Wrapper = styled.div`
}
&.border-top {
border-top: solid 1px ${(props) => props.theme.dropdown.seperator};
border-top: solid 1px ${(props) => props.theme.dropdown.separator};
}
}
}

View File

@@ -5,7 +5,16 @@ import StyledWrapper from './StyledWrapper';
const Dropdown = ({ icon, children, onCreate, placement }) => {
return (
<StyledWrapper className="dropdown">
<Tippy content={children} placement={placement || 'bottom-end'} animation={false} arrow={false} onCreate={onCreate} interactive={true} trigger="click" appendTo="parent">
<Tippy
content={children}
placement={placement || 'bottom-end'}
animation={false}
arrow={false}
onCreate={onCreate}
interactive={true}
trigger="click"
appendTo="parent"
>
{icon}
</Tippy>
</StyledWrapper>

View File

@@ -2,7 +2,7 @@ import styled from 'styled-components';
const Wrapper = styled.div`
.current-enviroment {
background-color: ${(props) => props.theme.sidebar.badge.bg};
background-color: ${(props) => props.theme.sidebar.badge.bg};
border-radius: 15px;
.caret {

View File

@@ -35,7 +35,7 @@ const EnvironmentSelector = ({ collection }) => {
toast.success(`No Environments are active now`);
}
})
.catch((err) => console.log(err) && toast.error('An error occured while selecting the environment'));
.catch((err) => console.log(err) && toast.error('An error occurred while selecting the environment'));
};
return (
@@ -64,7 +64,7 @@ const EnvironmentSelector = ({ collection }) => {
}}
>
<IconDatabaseOff size={18} strokeWidth={1.5} />
<span className='ml-2'>No Environment</span>
<span className="ml-2">No Environment</span>
</div>
<div className="dropdown-item border-top" onClick={() => setOpenSettingsModal(true)}>
<div className="pr-2 text-gray-600">

View File

@@ -0,0 +1,75 @@
import Modal from 'components/Modal/index';
import Portal from 'components/Portal/index';
import { useFormik } from 'formik';
import { copyEnvironment } from 'providers/ReduxStore/slices/collections/actions';
import { useEffect, useRef } from 'react';
import toast from 'react-hot-toast';
import { useDispatch } from 'react-redux';
import * as Yup from 'yup';
const CopyEnvironment = ({ collection, environment, onClose }) => {
const dispatch = useDispatch();
const inputRef = useRef();
const formik = useFormik({
enableReinitialize: true,
initialValues: {
name: environment.name + ' - Copy'
},
validationSchema: Yup.object({
name: Yup.string()
.min(1, 'must be at least 1 character')
.max(50, 'must be 50 characters or less')
.required('name is required')
}),
onSubmit: (values) => {
dispatch(copyEnvironment(values.name, environment.uid, collection.uid))
.then(() => {
toast.success('Environment created in collection');
onClose();
})
.catch(() => toast.error('An error occurred while created the environment'));
}
});
useEffect(() => {
if (inputRef && inputRef.current) {
inputRef.current.focus();
}
}, [inputRef]);
const onSubmit = () => {
formik.handleSubmit();
};
return (
<Portal>
<Modal size="sm" title={'Copy Environment'} confirmText="Copy" handleConfirm={onSubmit} handleCancel={onClose}>
<form className="bruno-form" onSubmit={formik.handleSubmit}>
<div>
<label htmlFor="name" className="block font-semibold">
New Environment Name
</label>
<input
id="environment-name"
type="text"
name="name"
ref={inputRef}
className="block textbox mt-2 w-full"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
onChange={formik.handleChange}
value={formik.values.name || ''}
/>
{formik.touched.name && formik.errors.name ? (
<div className="text-red-500">{formik.errors.name}</div>
) : null}
</div>
</form>
</Modal>
</Portal>
);
};
export default CopyEnvironment;

View File

@@ -16,7 +16,10 @@ const CreateEnvironment = ({ collection, onClose }) => {
name: ''
},
validationSchema: Yup.object({
name: Yup.string().min(1, 'must be atleast 1 characters').max(50, 'must be 50 characters or less').required('name is required')
name: Yup.string()
.min(1, 'must be at least 1 character')
.max(50, 'must be 50 characters or less')
.required('name is required')
}),
onSubmit: (values) => {
dispatch(addEnvironment(values.name, collection.uid))
@@ -24,7 +27,7 @@ const CreateEnvironment = ({ collection, onClose }) => {
toast.success('Environment created in collection');
onClose();
})
.catch(() => toast.error('An error occured while created the environment'));
.catch(() => toast.error('An error occurred while created the environment'));
}
});
@@ -40,7 +43,13 @@ const CreateEnvironment = ({ collection, onClose }) => {
return (
<Portal>
<Modal size="sm" title={'Create Environment'} confirmText="Create" handleConfirm={onSubmit} handleCancel={onClose}>
<Modal
size="sm"
title={'Create Environment'}
confirmText="Create"
handleConfirm={onSubmit}
handleCancel={onClose}
>
<form className="bruno-form" onSubmit={formik.handleSubmit}>
<div>
<label htmlFor="name" className="block font-semibold">
@@ -59,7 +68,9 @@ const CreateEnvironment = ({ collection, onClose }) => {
onChange={formik.handleChange}
value={formik.values.name || ''}
/>
{formik.touched.name && formik.errors.name ? <div className="text-red-500">{formik.errors.name}</div> : null}
{formik.touched.name && formik.errors.name ? (
<div className="text-red-500">{formik.errors.name}</div>
) : null}
</div>
</form>
</Modal>

View File

@@ -14,13 +14,19 @@ const DeleteEnvironment = ({ onClose, environment, collection }) => {
toast.success('Environment deleted successfully');
onClose();
})
.catch(() => toast.error('An error occured while deleting the environment'));
.catch(() => toast.error('An error occurred while deleting the environment'));
};
return (
<Portal>
<StyledWrapper>
<Modal size="sm" title={'Delete Environment'} confirmText="Delete" handleConfirm={onConfirm} handleCancel={onClose}>
<Modal
size="sm"
title={'Delete Environment'}
confirmText="Delete"
handleConfirm={onConfirm}
handleCancel={onClose}
>
Are you sure you want to delete <span className="font-semibold">{environment.name}</span> ?
</Modal>
</StyledWrapper>

View File

@@ -5,18 +5,30 @@ const Wrapper = styled.div`
width: 100%;
border-collapse: collapse;
font-weight: 600;
table-layout: fixed;
thead,
td {
border: 1px solid ${(props) => props.theme.collection.environment.settings.gridBorder};
padding: 4px 10px;
&:nth-child(1),
&:nth-child(4),
&:nth-child(5) {
width: 70px;
}
&:nth-child(2) {
width: 25%;
}
}
thead {
color: ${(props) => props.theme.table.thead.color};;
color: ${(props) => props.theme.table.thead.color};
font-size: 0.8125rem;
user-select: none;
}
td {
thead td {
padding: 6px 10px;
}
}

View File

@@ -2,13 +2,16 @@ import React, { useReducer } 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';
import { saveEnvironment } from 'providers/ReduxStore/slices/collections/actions';
import reducer from './reducer';
import SingleLineEditor from 'components/SingleLineEditor';
import StyledWrapper from './StyledWrapper';
const EnvironmentVariables = ({ environment, collection }) => {
const dispatch = useDispatch();
const { storedTheme } = useTheme();
const [state, reducerDispatch] = useReducer(reducer, { hasChanges: false, variables: environment.variables || [] });
const { variables, hasChanges } = state;
@@ -20,7 +23,7 @@ const EnvironmentVariables = ({ environment, collection }) => {
type: 'CHANGES_SAVED'
});
})
.catch(() => toast.error('An error occured while saving the changes'));
.catch(() => toast.error('An error occurred while saving the changes'));
};
const addVariable = () => {
@@ -44,6 +47,10 @@ const EnvironmentVariables = ({ environment, collection }) => {
variable.enabled = e.target.checked;
break;
}
case 'secret': {
variable.secret = e.target.checked;
break;
}
}
reducerDispatch({
type: 'UPDATE_VAR',
@@ -63,8 +70,10 @@ const EnvironmentVariables = ({ environment, collection }) => {
<table>
<thead>
<tr>
<td>Enabled</td>
<td>Name</td>
<td>Value</td>
<td>Secret</td>
<td></td>
</tr>
</thead>
@@ -73,6 +82,14 @@ const EnvironmentVariables = ({ environment, collection }) => {
? variables.map((variable, index) => {
return (
<tr key={variable.uid}>
<td className="text-center">
<input
type="checkbox"
checked={variable.enabled}
className="mr-3 mousetrap"
onChange={(e) => handleVarChange(e, variable, 'enabled')}
/>
</td>
<td>
<input
type="text"
@@ -86,24 +103,25 @@ const EnvironmentVariables = ({ environment, collection }) => {
/>
</td>
<td>
<input
type="text"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
<SingleLineEditor
value={variable.value}
className="mousetrap"
onChange={(e) => handleVarChange(e, variable, 'value')}
theme={storedTheme}
onChange={(newValue) => handleVarChange({ target: { value: newValue } }, variable, 'value')}
collection={collection}
/>
</td>
<td>
<div className="flex items-center">
<input type="checkbox" checked={variable.enabled} className="mr-3 mousetrap" onChange={(e) => handleVarChange(e, variable, 'enabled')} />
<button onClick={() => handleRemoveVars(variable)}>
<IconTrash strokeWidth={1.5} size={20} />
</button>
</div>
<input
type="checkbox"
checked={variable.secret}
className="mr-3 mousetrap"
onChange={(e) => handleVarChange(e, variable, 'secret')}
/>
</td>
<td>
<button onClick={() => handleRemoveVars(variable)}>
<IconTrash strokeWidth={1.5} size={20} />
</button>
</td>
</tr>
);
@@ -119,7 +137,12 @@ const EnvironmentVariables = ({ environment, collection }) => {
</div>
<div>
<button type="submit" className="submit btn btn-md btn-secondary mt-2" disabled={!hasChanges} onClick={saveChanges}>
<button
type="submit"
className="submit btn btn-md btn-secondary mt-2"
disabled={!hasChanges}
onClick={saveChanges}
>
Save
</button>
</div>

View File

@@ -12,6 +12,7 @@ const reducer = (state, action) => {
name: '',
value: '',
type: 'text',
secret: false,
enabled: true
});
draft.hasChanges = true;
@@ -24,6 +25,7 @@ const reducer = (state, action) => {
variable.name = action.variable.name;
variable.value = action.variable.value;
variable.enabled = action.variable.enabled;
variable.secret = action.variable.secret;
draft.hasChanges = true;
});
}

View File

@@ -1,17 +1,30 @@
import React, { useState } from 'react';
import { IconEdit, IconTrash, IconDatabase } from '@tabler/icons';
import EnvironmentVariables from './EnvironmentVariables';
import RenameEnvironment from '../../RenameEnvironment';
import { IconCopy, IconDatabase, IconEdit, IconTrash } from '@tabler/icons';
import { useState } from 'react';
import CopyEnvironment from '../../CopyEnvironment';
import DeleteEnvironment from '../../DeleteEnvironment';
import RenameEnvironment from '../../RenameEnvironment';
import EnvironmentVariables from './EnvironmentVariables';
const EnvironmentDetails = ({ environment, collection }) => {
const [openEditModal, setOpenEditModal] = useState(false);
const [openDeleteModal, setOpenDeleteModal] = useState(false);
const [openCopyModal, setOpenCopyModal] = useState(false);
return (
<div className="px-6 flex-grow flex flex-col pt-6" style={{ maxWidth: '700px' }}>
{openEditModal && <RenameEnvironment onClose={() => setOpenEditModal(false)} environment={environment} collection={collection} />}
{openDeleteModal && <DeleteEnvironment onClose={() => setOpenDeleteModal(false)} environment={environment} collection={collection} />}
{openEditModal && (
<RenameEnvironment onClose={() => setOpenEditModal(false)} environment={environment} collection={collection} />
)}
{openDeleteModal && (
<DeleteEnvironment
onClose={() => setOpenDeleteModal(false)}
environment={environment}
collection={collection}
/>
)}
{openCopyModal && (
<CopyEnvironment onClose={() => setOpenCopyModal(false)} environment={environment} collection={collection} />
)}
<div className="flex">
<div className="flex flex-grow items-center">
<IconDatabase className="cursor-pointer" size={20} strokeWidth={1.5} />
@@ -19,6 +32,7 @@ const EnvironmentDetails = ({ environment, collection }) => {
</div>
<div className="flex gap-x-4 pl-4">
<IconEdit className="cursor-pointer" size={20} strokeWidth={1.5} onClick={() => setOpenEditModal(true)} />
<IconCopy className="cursor-pointer" size={20} strokeWidth={1.5} onClick={() => setOpenCopyModal(true)} />
<IconTrash className="cursor-pointer" size={20} strokeWidth={1.5} onClick={() => setOpenDeleteModal(true)} />
</div>
</div>

View File

@@ -14,12 +14,12 @@ const EnvironmentList = ({ collection }) => {
const prevEnvUids = usePrevious(envUids);
useEffect(() => {
if(selectedEnvironment) {
if (selectedEnvironment) {
return;
}
const environment = findEnvironmentInCollection(collection, collection.activeEnvironmentUid);
if(environment) {
if (environment) {
setSelectedEnvironment(environment);
} else {
setSelectedEnvironment(environments && environments.length ? environments[0] : null);
@@ -28,9 +28,9 @@ const EnvironmentList = ({ collection }) => {
useEffect(() => {
// check env add
if (prevEnvUids && prevEnvUids.length && envUids.length > prevEnvUids.length) {
if (prevEnvUids && prevEnvUids.length && envUids.length > prevEnvUids.length) {
const newEnv = environments.find((env) => !prevEnvUids.includes(env.uid));
if(newEnv){
if (newEnv) {
setSelectedEnvironment(newEnv);
}
}
@@ -38,7 +38,7 @@ const EnvironmentList = ({ collection }) => {
// check env delete
if (prevEnvUids && prevEnvUids.length && envUids.length < prevEnvUids.length) {
setSelectedEnvironment(environments && environments.length ? environments[0] : null);
}
}
}, [envUids, environments, prevEnvUids]);
if (!selectedEnvironment) {

View File

@@ -16,7 +16,10 @@ const RenameEnvironment = ({ onClose, environment, collection }) => {
name: environment.name
},
validationSchema: Yup.object({
name: Yup.string().min(1, 'must be atleast 1 characters').max(50, 'must be 50 characters or less').required('name is required')
name: Yup.string()
.min(1, 'must be at least 1 character')
.max(50, 'must be 50 characters or less')
.required('name is required')
}),
onSubmit: (values) => {
dispatch(renameEnvironment(values.name, environment.uid, collection.uid))
@@ -24,7 +27,7 @@ const RenameEnvironment = ({ onClose, environment, collection }) => {
toast.success('Environment renamed successfully');
onClose();
})
.catch(() => toast.error('An error occured while renaming the environment'));
.catch(() => toast.error('An error occurred while renaming the environment'));
}
});
@@ -40,7 +43,13 @@ const RenameEnvironment = ({ onClose, environment, collection }) => {
return (
<Portal>
<Modal size="sm" title={'Rename Environment'} confirmText="Rename" handleConfirm={onSubmit} handleCancel={onClose}>
<Modal
size="sm"
title={'Rename Environment'}
confirmText="Rename"
handleConfirm={onSubmit}
handleCancel={onClose}
>
<form className="bruno-form" onSubmit={formik.handleSubmit}>
<div>
<label htmlFor="name" className="block font-semibold">
@@ -59,7 +68,9 @@ const RenameEnvironment = ({ onClose, environment, collection }) => {
onChange={formik.handleChange}
value={formik.values.name || ''}
/>
{formik.touched.name && formik.errors.name ? <div className="text-red-500">{formik.errors.name}</div> : null}
{formik.touched.name && formik.errors.name ? (
<div className="text-red-500">{formik.errors.name}</div>
) : null}
</div>
</form>
</Modal>

View File

@@ -11,11 +11,21 @@ const EnvironmentSettings = ({ collection, onClose }) => {
if (!environments || !environments.length) {
return (
<StyledWrapper>
<Modal size="md" title="Environments" confirmText={'Close'} handleConfirm={onClose} handleCancel={onClose} hideCancel={true}>
<Modal
size="md"
title="Environments"
confirmText={'Close'}
handleConfirm={onClose}
handleCancel={onClose}
hideCancel={true}
>
{openCreateModal && <CreateEnvironment collection={collection} onClose={() => setOpenCreateModal(false)} />}
<div className="text-center">
<p>No environments found!</p>
<button className="btn-create-environment text-link pr-2 py-3 mt-2 select-none" onClick={() => setOpenCreateModal(true)}>
<button
className="btn-create-environment text-link pr-2 py-3 mt-2 select-none"
onClick={() => setOpenCreateModal(true)}
>
+ <span>Create Environment</span>
</button>
</div>

View File

@@ -1,17 +1,12 @@
import React from 'react';
const SendIcon = ({color, width}) => {
const SendIcon = ({ color, width }) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={width}
height={width}
viewBox="0 0 48 48"
>
<path fill={color} d="M4.02 42l41.98-18-41.98-18-.02 14 30 4-30 4z"/>
<path d="M0 0h48v48h-48z" fill="none"/>
<svg xmlns="http://www.w3.org/2000/svg" width={width} height={width} viewBox="0 0 48 48">
<path fill={color} d="M4.02 42l41.98-18-41.98-18-.02 14 30 4-30 4z" />
<path d="M0 0h48v48h-48z" fill="none" />
</svg>
);
}
};
export default SendIcon;

View File

@@ -1,6 +1,8 @@
import styled from 'styled-components';
const Wrapper = styled.div`
color: ${(props) => props.theme.text};
&.modal--animate-out {
animation: fade-out 0.5s forwards cubic-bezier(0.19, 1, 0.22, 1);
@@ -19,7 +21,8 @@ const Wrapper = styled.div`
align-items: flex-start;
justify-content: center;
overflow-y: auto;
z-index: 1003;
z-index: 10;
background-color: rgba(0, 0, 0, 0.5);
}
.bruno-modal-card {
@@ -28,7 +31,7 @@ const Wrapper = styled.div`
background: var(--color-background-top);
border-radius: var(--border-radius);
position: relative;
z-index: 1003;
z-index: 10;
max-width: calc(100% - var(--spacing-base-unit));
box-shadow: var(--box-shadow-base);
display: flex;
@@ -100,7 +103,7 @@ const Wrapper = styled.div`
border-radius: 0px;
outline: none;
box-shadow: none;
transition: border-color ease-in-out .1s;
transition: border-color ease-in-out 0.1s;
border-radius: 3px;
background-color: ${(props) => props.theme.modal.input.bg};
border: 1px solid ${(props) => props.theme.modal.input.border};

View File

@@ -3,7 +3,7 @@ import StyledWrapper from './StyledWrapper';
const ModalHeader = ({ title, handleCancel }) => (
<div className="bruno-modal-header">
{title ? <div className="bruno-modal-heade-title">{title}</div> : null}
{title ? <div className="bruno-modal-header-title">{title}</div> : null}
{handleCancel ? (
<div className="close cursor-pointer" onClick={handleCancel ? () => handleCancel() : null}>
×
@@ -14,7 +14,15 @@ const ModalHeader = ({ title, handleCancel }) => (
const ModalContent = ({ children }) => <div className="bruno-modal-content px-4 py-6">{children}</div>;
const ModalFooter = ({ confirmText, cancelText, handleSubmit, handleCancel, confirmDisabled, hideCancel, hideFooter }) => {
const ModalFooter = ({
confirmText,
cancelText,
handleSubmit,
handleCancel,
confirmDisabled,
hideCancel,
hideFooter
}) => {
confirmText = confirmText || 'Save';
cancelText = cancelText || 'Cancel';
@@ -30,7 +38,12 @@ const ModalFooter = ({ confirmText, cancelText, handleSubmit, handleCancel, conf
</button>
</span>
<span>
<button type="submit" className="submit btn btn-md btn-secondary" disabled={confirmDisabled} onClick={handleSubmit}>
<button
type="submit"
className="submit btn btn-md btn-secondary"
disabled={confirmDisabled}
onClick={handleSubmit}
>
{confirmText}
</button>
</span>
@@ -38,18 +51,30 @@ const ModalFooter = ({ confirmText, cancelText, handleSubmit, handleCancel, conf
);
};
const Modal = ({ size, title, confirmText, cancelText, handleCancel, handleConfirm, children, confirmDisabled, hideCancel, hideFooter }) => {
const Modal = ({
size,
title,
confirmText,
cancelText,
handleCancel,
handleConfirm,
children,
confirmDisabled,
hideCancel,
hideFooter,
closeModalFadeTimeout = 500
}) => {
const [isClosing, setIsClosing] = useState(false);
const escFunction = (event) => {
const escKeyCode = 27;
if (event.keyCode === escKeyCode) {
closeModal();
closeModal({ type: 'esc' });
}
};
const closeModal = () => {
const closeModal = (args) => {
setIsClosing(true);
setTimeout(() => handleCancel(), 500);
setTimeout(() => handleCancel(args), closeModalFadeTimeout);
};
useEffect(() => {
@@ -64,18 +89,18 @@ const Modal = ({ size, title, confirmText, cancelText, handleCancel, handleConfi
if (isClosing) {
classes += ' modal--animate-out';
}
if(hideFooter) {
if (hideFooter) {
classes += ' modal-footer-none';
}
return (
<StyledWrapper className={classes}>
<div className={`bruno-modal-card modal-${size}`}>
<ModalHeader title={title} handleCancel={() => closeModal()} />
<ModalHeader title={title} handleCancel={() => closeModal({ type: 'icon' })} />
<ModalContent>{children}</ModalContent>
<ModalFooter
confirmText={confirmText}
cancelText={cancelText}
handleCancel={() => closeModal()}
handleCancel={() => closeModal({ type: 'button' })}
handleSubmit={handleConfirm}
confirmDisabled={confirmDisabled}
hideCancel={hideCancel}
@@ -84,7 +109,12 @@ const Modal = ({ size, title, confirmText, cancelText, handleCancel, handleConfi
</div>
{/* Clicking on backdrop closes the modal */}
<div className="bruno-modal-backdrop" onClick={() => closeModal()} />
<div
className="bruno-modal-backdrop"
onClick={() => {
closeModal({ type: 'backdrop' });
}}
/>
</StyledWrapper>
);
};

View File

@@ -0,0 +1,7 @@
import styled from 'styled-components';
const StyledWrapper = styled.div`
color: ${(props) => props.theme.text};
`;
export default StyledWrapper;

View File

@@ -0,0 +1,38 @@
import React, { useState } from 'react';
import { usePreferences } from 'providers/Preferences';
import StyledWrapper from './StyledWrapper';
const General = () => {
const { preferences, setPreferences } = usePreferences();
const [sslVerification, setSslVerification] = useState(preferences.request.sslVerification);
const handleCheckboxChange = () => {
const updatedPreferences = {
...preferences,
request: {
...preferences.request,
sslVerification: !sslVerification
}
};
setPreferences(updatedPreferences)
.then(() => {
setSslVerification(!sslVerification);
})
.catch((err) => {
console.error(err);
});
};
return (
<StyledWrapper>
<div className="flex items-center mt-2">
<input type="checkbox" checked={sslVerification} onChange={handleCheckboxChange} className="mr-3 mousetrap" />
SSL Certificate Verification
</div>
</StyledWrapper>
);
};
export default General;

View File

@@ -0,0 +1,36 @@
import styled from 'styled-components';
const StyledWrapper = styled.div`
div.tabs {
margin-top: -0.5rem;
div.tab {
padding: 6px 0px;
border: none;
border-bottom: solid 2px transparent;
margin-right: 1.25rem;
color: var(--color-tab-inactive);
cursor: pointer;
&:focus,
&:active,
&:focus-within,
&:focus-visible,
&:target {
outline: none !important;
box-shadow: none !important;
}
&.active {
color: ${(props) => props.theme.tabs.active.color} !important;
border-bottom: solid 2px ${(props) => props.theme.tabs.active.border} !important;
}
}
}
section.tab-panel {
min-height: 300px;
}
`;
export default StyledWrapper;

View File

@@ -1,8 +1,8 @@
import styled from 'styled-components';
const StyledWrapper = styled.div`
color: var(--color-text);
.collection-options {
color: ${(props) => props.theme.text};
.rows {
svg {
position: relative;
top: -1px;

View File

@@ -0,0 +1,44 @@
import React from 'react';
import { IconSpeakerphone, IconBrandTwitter, IconBrandGithub, IconBrandDiscord, IconBook } from '@tabler/icons';
import StyledWrapper from './StyledWrapper';
const Support = () => {
return (
<StyledWrapper>
<div className="rows">
<div className="mt-2">
<a href="https://docs.usebruno.com" target="_blank" className="flex items-end">
<IconBook size={18} strokeWidth={2} />
<span className="label ml-2">Documentation</span>
</a>
</div>
<div className="mt-2">
<a href="https://github.com/usebruno/bruno/issues" target="_blank" className="flex items-end">
<IconSpeakerphone size={18} strokeWidth={2} />
<span className="label ml-2">Report Issues</span>
</a>
</div>
<div className="mt-2">
<a href="https://discord.com/invite/KgcZUncpjq" target="_blank" className="flex items-end">
<IconBrandDiscord size={18} strokeWidth={2} />
<span className="label ml-2">Discord</span>
</a>
</div>
<div className="mt-2">
<a href="https://github.com/usebruno/bruno" target="_blank" className="flex items-end">
<IconBrandGithub size={18} strokeWidth={2} />
<span className="label ml-2">GitHub</span>
</a>
</div>
<div className="mt-2">
<a href="https://twitter.com/use_bruno" target="_blank" className="flex items-end">
<IconBrandTwitter size={18} strokeWidth={2} />
<span className="label ml-2">Twitter</span>
</a>
</div>
</div>
</StyledWrapper>
);
};
export default Support;

View File

@@ -0,0 +1,7 @@
import styled from 'styled-components';
const StyledWrapper = styled.div`
color: var(--color-text);
`;
export default StyledWrapper;

View File

@@ -0,0 +1,64 @@
import React from 'react';
import { useFormik } from 'formik';
import * as Yup from 'yup';
import StyledWrapper from './StyledWrapper';
import { useTheme } from 'providers/Theme';
const Theme = () => {
const { storedTheme, setStoredTheme } = useTheme();
const formik = useFormik({
enableReinitialize: true,
initialValues: {
theme: storedTheme
},
validationSchema: Yup.object({
theme: Yup.string().oneOf(['light', 'dark']).required('theme is required')
}),
onSubmit: (values) => {
setStoredTheme(values.theme);
}
});
return (
<StyledWrapper>
<div className="bruno-form">
<div className="flex items-center mt-2">
<input
id="light-theme"
className="cursor-pointer"
type="radio"
name="theme"
onChange={(e) => {
formik.handleChange(e);
formik.handleSubmit();
}}
value="light"
checked={formik.values.theme === 'light'}
/>
<label htmlFor="light-theme" className="ml-1 cursor-pointer select-none">
Light
</label>
<input
id="dark-theme"
className="ml-4 cursor-pointer"
type="radio"
name="theme"
onChange={(e) => {
formik.handleChange(e);
formik.handleSubmit();
}}
value="dark"
checked={formik.values.theme === 'dark'}
/>
<label htmlFor="dark-theme" className="ml-1 cursor-pointer select-none">
Dark
</label>
</div>
</div>
</StyledWrapper>
);
};
export default Theme;

View File

@@ -0,0 +1,54 @@
import Modal from 'components/Modal/index';
import classnames from 'classnames';
import React, { useState } from 'react';
import Support from './Support';
import General from './General';
import Theme from './Theme';
import StyledWrapper from './StyledWrapper';
const Preferences = ({ onClose }) => {
const [tab, setTab] = useState('general');
const getTabClassname = (tabName) => {
return classnames(`tab select-none ${tabName}`, {
active: tabName === tab
});
};
const getTabPanel = (tab) => {
switch (tab) {
case 'general': {
return <General />;
}
case 'theme': {
return <Theme />;
}
case 'support': {
return <Support />;
}
}
};
return (
<StyledWrapper>
<Modal size="lg" title="Preferences" handleCancel={onClose} hideFooter={true}>
<div className="flex items-center px-2 tabs" role="tablist">
<div className={getTabClassname('general')} role="tab" onClick={() => setTab('general')}>
General
</div>
<div className={getTabClassname('theme')} role="tab" onClick={() => setTab('theme')}>
Theme
</div>
<div className={getTabClassname('support')} role="tab" onClick={() => setTab('support')}>
Support
</div>
</div>
<section className="flex flex-grow px-2 mt-4 tab-panel">{getTabPanel(tab)}</section>
</Modal>
</StyledWrapper>
);
};
export default Preferences;

View File

@@ -2,7 +2,7 @@ import React from 'react';
/**
* Assertion operators
*
*
* eq : equal to
* neq : not equal to
* gt : greater than
@@ -33,10 +33,32 @@ import React from 'react';
const AssertionOperator = ({ operator, onChange }) => {
const operators = [
'eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'in', 'notIn',
'contains', 'notContains', 'length', 'matches', 'notMatches',
'startsWith', 'endsWith', 'between', 'isEmpty', 'isNull', 'isUndefined',
'isDefined', 'isTruthy', 'isFalsy', 'isJson', 'isNumber', 'isString', 'isBoolean'
'eq',
'neq',
'gt',
'gte',
'lt',
'lte',
'in',
'notIn',
'contains',
'notContains',
'length',
'matches',
'notMatches',
'startsWith',
'endsWith',
'between',
'isEmpty',
'isNull',
'isUndefined',
'isDefined',
'isTruthy',
'isFalsy',
'isJson',
'isNumber',
'isString',
'isBoolean'
];
const handleChange = (e) => {
@@ -44,7 +66,7 @@ const AssertionOperator = ({ operator, onChange }) => {
};
const getLabel = (operator) => {
switch(operator) {
switch (operator) {
case 'eq':
return 'equals';
case 'neq':

View File

@@ -6,7 +6,7 @@ import { useTheme } from 'providers/Theme';
/**
* Assertion operators
*
*
* eq : equal to
* neq : not equal to
* gt : greater than
@@ -35,7 +35,7 @@ import { useTheme } from 'providers/Theme';
* isBoolean : is boolean
*/
const parseAssertionOperator = (str = '') => {
if(!str || typeof str !== 'string' || !str.length) {
if (!str || typeof str !== 'string' || !str.length) {
return {
operator: 'eq',
value: str
@@ -43,27 +43,58 @@ const parseAssertionOperator = (str = '') => {
}
const operators = [
'eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'in', 'notIn',
'contains', 'notContains', 'length', 'matches', 'notMatches',
'startsWith', 'endsWith', 'between', 'isEmpty', 'isNull', 'isUndefined',
'isDefined', 'isTruthy', 'isFalsy', 'isJson', 'isNumber', 'isString', 'isBoolean'
'eq',
'neq',
'gt',
'gte',
'lt',
'lte',
'in',
'notIn',
'contains',
'notContains',
'length',
'matches',
'notMatches',
'startsWith',
'endsWith',
'between',
'isEmpty',
'isNull',
'isUndefined',
'isDefined',
'isTruthy',
'isFalsy',
'isJson',
'isNumber',
'isString',
'isBoolean'
];
const unaryOperators = [
'isEmpty', 'isNull', 'isUndefined', 'isDefined', 'isTruthy', 'isFalsy', 'isJson', 'isNumber', 'isString', 'isBoolean'
'isEmpty',
'isNull',
'isUndefined',
'isDefined',
'isTruthy',
'isFalsy',
'isJson',
'isNumber',
'isString',
'isBoolean'
];
const [operator, ...rest] = str.trim().split(' ');
const value = rest.join(' ');
if(unaryOperators.includes(operator)) {
if (unaryOperators.includes(operator)) {
return {
operator,
value: ''
};
}
if(operators.includes(operator)) {
if (operators.includes(operator)) {
return {
operator,
value
@@ -78,22 +109,33 @@ const parseAssertionOperator = (str = '') => {
const isUnaryOperator = (operator) => {
const unaryOperators = [
'isEmpty', 'isNull', 'isUndefined', 'isDefined', 'isTruthy', 'isFalsy', 'isJson', 'isNumber', 'isString', 'isBoolean'
'isEmpty',
'isNull',
'isUndefined',
'isDefined',
'isTruthy',
'isFalsy',
'isJson',
'isNumber',
'isString',
'isBoolean'
];
return unaryOperators.includes(operator);
};
const AssertionRow = ({
item, collection, assertion, handleAssertionChange, handleRemoveAssertion,
onSave, handleRun
item,
collection,
assertion,
handleAssertionChange,
handleRemoveAssertion,
onSave,
handleRun
}) => {
const { storedTheme } = useTheme();
const {
operator,
value
} = parseAssertionOperator(assertion.value);
const { operator, value } = parseAssertionOperator(assertion.value);
return (
<tr key={assertion.uid}>
@@ -112,11 +154,17 @@ const AssertionRow = ({
<td>
<AssertionOperator
operator={operator}
onChange={(op) => handleAssertionChange({
target: {
value: `${op} ${value}`
}
}, assertion, 'value')}
onChange={(op) =>
handleAssertionChange(
{
target: {
value: `${op} ${value}`
}
},
assertion,
'value'
)
}
/>
</td>
<td>
@@ -126,20 +174,22 @@ const AssertionRow = ({
theme={storedTheme}
readOnly={true}
onSave={onSave}
onChange={(newValue) => handleAssertionChange({
target: {
value: newValue
}
}, assertion, 'value')}
onChange={(newValue) =>
handleAssertionChange(
{
target: {
value: newValue
}
},
assertion,
'value'
)
}
onRun={handleRun}
collection={collection}
/>
) : (
<input
type="text"
className='cursor-default'
disabled
/>
<input type="text" className="cursor-default" disabled />
)}
</td>
<td>
@@ -147,10 +197,11 @@ const AssertionRow = ({
<input
type="checkbox"
checked={assertion.enabled}
tabIndex="-1"
className="mr-3 mousetrap"
onChange={(e) => handleAssertionChange(e, assertion, 'enabled')}
/>
<button onClick={() => handleRemoveAssertion(assertion)}>
<button tabIndex="-1" onClick={() => handleRemoveAssertion(assertion)}>
<IconTrash strokeWidth={1.5} size={20} />
</button>
</div>

View File

@@ -27,6 +27,10 @@ const Wrapper = styled.div`
&:nth-child(4) {
width: 70px;
}
select {
background-color: transparent;
}
}
}

View File

@@ -0,0 +1,28 @@
import styled from 'styled-components';
const Wrapper = styled.div`
font-size: 0.8125rem;
.auth-mode-selector {
background: transparent;
.auth-mode-label {
color: ${(props) => props.theme.colors.text.yellow};
}
.dropdown-item {
padding: 0.2rem 0.6rem !important;
}
.label-item {
padding: 0.2rem 0.6rem !important;
}
}
.caret {
color: rgb(140, 140, 140);
fill: rgb(140 140 140);
}
`;
export default Wrapper;

View File

@@ -0,0 +1,70 @@
import React, { useRef, forwardRef } from 'react';
import get from 'lodash/get';
import { IconCaretDown } from '@tabler/icons';
import Dropdown from 'components/Dropdown';
import { useDispatch } from 'react-redux';
import { updateRequestAuthMode } from 'providers/ReduxStore/slices/collections';
import { humanizeRequestAuthMode } from 'utils/collections';
import StyledWrapper from './StyledWrapper';
const AuthMode = ({ item, collection }) => {
const dispatch = useDispatch();
const dropdownTippyRef = useRef();
const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref);
const authMode = item.draft ? get(item, 'draft.request.auth.mode') : get(item, 'request.auth.mode');
const Icon = forwardRef((props, ref) => {
return (
<div ref={ref} className="flex items-center justify-center auth-mode-label select-none">
{humanizeRequestAuthMode(authMode)} <IconCaretDown className="caret ml-1 mr-1" size={14} strokeWidth={2} />
</div>
);
});
const onModeChange = (value) => {
dispatch(
updateRequestAuthMode({
itemUid: item.uid,
collectionUid: collection.uid,
mode: value
})
);
};
return (
<StyledWrapper>
<div className="inline-flex items-center cursor-pointer auth-mode-selector">
<Dropdown onCreate={onDropdownCreate} icon={<Icon />} placement="bottom-end">
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
onModeChange('basic');
}}
>
Basic Auth
</div>
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
onModeChange('bearer');
}}
>
Bearer Token
</div>
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
onModeChange('none');
}}
>
No Auth
</div>
</Dropdown>
</div>
</StyledWrapper>
);
};
export default AuthMode;

View File

@@ -0,0 +1,16 @@
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};
}
`;
export default Wrapper;

View File

@@ -0,0 +1,76 @@
import React from 'react';
import get from 'lodash/get';
import { useTheme } from 'providers/Theme';
import { useDispatch } from 'react-redux';
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';
const BasicAuth = ({ item, collection }) => {
const dispatch = useDispatch();
const { storedTheme } = useTheme();
const basicAuth = item.draft ? get(item, 'draft.request.auth.basic', {}) : get(item, 'request.auth.basic', {});
const handleRun = () => dispatch(sendRequest(item, collection.uid));
const handleSave = () => dispatch(saveRequest(item.uid, collection.uid));
const handleUsernameChange = (username) => {
dispatch(
updateAuth({
mode: 'basic',
collectionUid: collection.uid,
itemUid: item.uid,
content: {
username: username,
password: basicAuth.password
}
})
);
};
const handlePasswordChange = (password) => {
dispatch(
updateAuth({
mode: 'basic',
collectionUid: collection.uid,
itemUid: item.uid,
content: {
username: basicAuth.username,
password: password
}
})
);
};
return (
<StyledWrapper className="mt-2 w-full">
<label className="block font-medium mb-2">Username</label>
<div className="single-line-editor-wrapper mb-2">
<SingleLineEditor
value={basicAuth.username || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleUsernameChange(val)}
onRun={handleRun}
collection={collection}
/>
</div>
<label className="block font-medium mb-2">Password</label>
<div className="single-line-editor-wrapper">
<SingleLineEditor
value={basicAuth.password || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handlePasswordChange(val)}
onRun={handleRun}
collection={collection}
/>
</div>
</StyledWrapper>
);
};
export default BasicAuth;

View File

@@ -0,0 +1,16 @@
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};
}
`;
export default Wrapper;

View File

@@ -0,0 +1,51 @@
import React from 'react';
import get from 'lodash/get';
import { useTheme } from 'providers/Theme';
import { useDispatch } from 'react-redux';
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';
const BearerAuth = ({ item, collection }) => {
const dispatch = useDispatch();
const { storedTheme } = useTheme();
const bearerToken = item.draft
? get(item, 'draft.request.auth.bearer.token')
: get(item, 'request.auth.bearer.token');
const handleRun = () => dispatch(sendRequest(item, collection.uid));
const handleSave = () => dispatch(saveRequest(item.uid, collection.uid));
const handleTokenChange = (token) => {
dispatch(
updateAuth({
mode: 'bearer',
collectionUid: collection.uid,
itemUid: item.uid,
content: {
token: token
}
})
);
};
return (
<StyledWrapper className="mt-2 w-full">
<label className="block font-medium mb-2">Token</label>
<div className="single-line-editor-wrapper">
<SingleLineEditor
value={bearerToken}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleTokenChange(val)}
onRun={handleRun}
collection={collection}
/>
</div>
</StyledWrapper>
);
};
export default BearerAuth;

View File

@@ -0,0 +1,5 @@
import styled from 'styled-components';
const Wrapper = styled.div``;
export default Wrapper;

View File

@@ -0,0 +1,31 @@
import React from 'react';
import get from 'lodash/get';
import AuthMode from './AuthMode';
import BearerAuth from './BearerAuth';
import BasicAuth from './BasicAuth';
import StyledWrapper from './StyledWrapper';
const Auth = ({ item, collection }) => {
const authMode = item.draft ? get(item, 'draft.request.auth.mode') : get(item, 'request.auth.mode');
const getAuthView = () => {
switch (authMode) {
case 'basic': {
return <BasicAuth collection={collection} item={item} />;
}
case 'bearer': {
return <BearerAuth collection={collection} item={item} />;
}
}
};
return (
<StyledWrapper className="w-full mt-1">
<div className="flex flex-grow justify-start items-center">
<AuthMode item={item} collection={collection} />
</div>
{getAuthView()}
</StyledWrapper>
);
};
export default Auth;

View File

@@ -4,7 +4,11 @@ import cloneDeep from 'lodash/cloneDeep';
import { IconTrash } from '@tabler/icons';
import { useDispatch } from 'react-redux';
import { useTheme } from 'providers/Theme';
import { addFormUrlEncodedParam, updateFormUrlEncodedParam, deleteFormUrlEncodedParam } from 'providers/ReduxStore/slices/collections';
import {
addFormUrlEncodedParam,
updateFormUrlEncodedParam,
deleteFormUrlEncodedParam
} from 'providers/ReduxStore/slices/collections';
import SingleLineEditor from 'components/SingleLineEditor';
import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
import StyledWrapper from './StyledWrapper';
@@ -88,23 +92,35 @@ const FormUrlEncodedParams = ({ item, collection }) => {
/>
</td>
<td>
<SingleLineEditor
<SingleLineEditor
value={param.value}
theme={storedTheme}
onSave={onSave}
onChange={(newValue) => handleParamChange({
target: {
value: newValue
}
}, param, 'value')}
onChange={(newValue) =>
handleParamChange(
{
target: {
value: newValue
}
},
param,
'value'
)
}
onRun={handleRun}
collection={collection}
/>
</td>
<td>
<div className="flex items-center">
<input type="checkbox" checked={param.enabled} className="mr-3 mousetrap" onChange={(e) => handleParamChange(e, param, 'enabled')} />
<button onClick={() => handleRemoveParams(param)}>
<input
type="checkbox"
checked={param.enabled}
tabIndex="-1"
className="mr-3 mousetrap"
onChange={(e) => handleParamChange(e, param, 'enabled')}
/>
<button tabIndex="-1" onClick={() => handleRemoveParams(param)}>
<IconTrash strokeWidth={1.5} size={20} />
</button>
</div>

View File

@@ -6,6 +6,7 @@ import { IconRefresh, IconLoader2, IconBook, IconDownload } from '@tabler/icons'
import { useSelector, useDispatch } from 'react-redux';
import { updateRequestPaneTab } from 'providers/ReduxStore/slices/tabs';
import QueryEditor from 'components/RequestPane/QueryEditor';
import Auth from 'components/RequestPane/Auth';
import GraphQLVariables from 'components/RequestPane/GraphQLVariables';
import RequestHeaders from 'components/RequestPane/RequestHeaders';
import Vars from 'components/RequestPane/Vars';
@@ -24,29 +25,31 @@ const GraphQLRequestPane = ({ item, collection, leftPaneWidth, onSchemaLoad, tog
const tabs = useSelector((state) => state.tabs.tabs);
const activeTabUid = useSelector((state) => state.tabs.activeTabUid);
const query = item.draft ? get(item, 'draft.request.body.graphql.query') : get(item, 'request.body.graphql.query');
const variables = item.draft ? get(item, 'draft.request.body.graphql.variables') : get(item, 'request.body.graphql.variables');
const variables = item.draft
? get(item, 'draft.request.body.graphql.variables')
: get(item, 'request.body.graphql.variables');
const url = item.draft ? get(item, 'draft.request.url') : get(item, 'request.url');
const {
storedTheme
} = useTheme();
const { storedTheme } = useTheme();
const environment = findEnvironmentInCollection(collection, collection.activeEnvironmentUid);
const request = item.draft ? item.draft.request : item.request;
let {
schema,
loadSchema,
isLoading: isSchemaLoading,
error: schemaError
} = useGraphqlSchema(url, environment);
} = useGraphqlSchema(url, environment, request, collection);
const loadGqlSchema = () => {
if(!isSchemaLoading) {
if (!isSchemaLoading) {
loadSchema();
}
};
useEffect(() => {
if(onSchemaLoad) {
if (onSchemaLoad) {
onSchemaLoad(schema);
}
}, [schema]);
@@ -75,17 +78,19 @@ const GraphQLRequestPane = ({ item, collection, leftPaneWidth, onSchemaLoad, tog
const getTabPanel = (tab) => {
switch (tab) {
case 'query': {
return <QueryEditor
collection={collection}
theme={storedTheme}
schema={schema}
width={leftPaneWidth}
onSave={onSave}
value={query}
onRun={onRun}
onEdit={onQueryChange}
onClickReference={handleGqlClickReference}
/>;
return (
<QueryEditor
collection={collection}
theme={storedTheme}
schema={schema}
width={leftPaneWidth}
onSave={onSave}
value={query}
onRun={onRun}
onEdit={onQueryChange}
onClickReference={handleGqlClickReference}
/>
);
}
case 'variables': {
return <GraphQLVariables item={item} variables={variables} collection={collection} />;
@@ -93,6 +98,9 @@ const GraphQLRequestPane = ({ item, collection, leftPaneWidth, onSchemaLoad, tog
case 'headers': {
return <RequestHeaders item={item} collection={collection} />;
}
case 'auth': {
return <Auth item={item} collection={collection} />;
}
case 'vars': {
return <Vars item={item} collection={collection} />;
}
@@ -117,7 +125,7 @@ const GraphQLRequestPane = ({ item, collection, leftPaneWidth, onSchemaLoad, tog
const focusedTab = find(tabs, (t) => t.uid === activeTabUid);
if (!focusedTab || !focusedTab.uid || !focusedTab.requestPaneTab) {
return <div className="pb-4 px-4">An error occured!</div>;
return <div className="pb-4 px-4">An error occurred!</div>;
}
const getTabClassname = (tabName) => {
@@ -128,7 +136,7 @@ const GraphQLRequestPane = ({ item, collection, leftPaneWidth, onSchemaLoad, tog
return (
<StyledWrapper className="flex flex-col h-full relative">
<div className="flex items-center tabs" role="tablist">
<div className="flex flex-wrap items-center tabs" role="tablist">
<div className={getTabClassname('query')} role="tab" onClick={() => selectTab('query')}>
Query
</div>
@@ -138,6 +146,9 @@ const GraphQLRequestPane = ({ item, collection, leftPaneWidth, onSchemaLoad, tog
<div className={getTabClassname('headers')} role="tab" onClick={() => selectTab('headers')}>
Headers
</div>
<div className={getTabClassname('auth')} role="tab" onClick={() => selectTab('auth')}>
Auth
</div>
<div className={getTabClassname('vars')} role="tab" onClick={() => selectTab('vars')}>
Vars
</div>
@@ -150,20 +161,16 @@ const GraphQLRequestPane = ({ item, collection, leftPaneWidth, onSchemaLoad, tog
<div className={getTabClassname('tests')} role="tab" onClick={() => selectTab('tests')}>
Tests
</div>
<div className="flex flex-grow justify-end items-center" style={{fontSize: 13}}>
<div className='flex items-center cursor-pointer hover:underline' onClick={loadGqlSchema}>
{isSchemaLoading ? (
<IconLoader2 className="animate-spin" size={18} strokeWidth={1.5}/>
) : null}
{!isSchemaLoading && !schema ? <IconDownload size={18} strokeWidth={1.5}/> : null }
{!isSchemaLoading && schema ? <IconRefresh size={18} strokeWidth={1.5}/> : null }
<span className='ml-1'>Schema</span>
<div className="flex flex-grow justify-end items-center" style={{ fontSize: 13 }}>
<div className="flex items-center cursor-pointer hover:underline" onClick={loadGqlSchema}>
{isSchemaLoading ? <IconLoader2 className="animate-spin" size={18} strokeWidth={1.5} /> : null}
{!isSchemaLoading && !schema ? <IconDownload size={18} strokeWidth={1.5} /> : null}
{!isSchemaLoading && schema ? <IconRefresh size={18} strokeWidth={1.5} /> : null}
<span className="ml-1">Schema</span>
</div>
<div
className='flex items-center cursor-pointer hover:underline ml-2'
onClick={toggleDocs}
>
<IconBook size={18} strokeWidth={1.5} /><span className='ml-1'>Docs</span>
<div className="flex items-center cursor-pointer hover:underline ml-2" onClick={toggleDocs}>
<IconBook size={18} strokeWidth={1.5} />
<span className="ml-1">Docs</span>
</div>
</div>
</div>

View File

@@ -6,14 +6,14 @@ import { simpleHash } from 'utils/common';
const schemaHashPrefix = 'bruno.graphqlSchema';
const useGraphqlSchema = (endpoint, environment) => {
const useGraphqlSchema = (endpoint, environment, request, collection) => {
const localStorageKey = `${schemaHashPrefix}.${simpleHash(endpoint)}`;
const [error, setError] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [schema, setSchema] = useState(() => {
try {
const saved = localStorage.getItem(localStorageKey);
if(!saved) {
if (!saved) {
return null;
}
return buildClientSchema(JSON.parse(saved));
@@ -25,7 +25,7 @@ const useGraphqlSchema = (endpoint, environment) => {
const loadSchema = () => {
setIsLoading(true);
fetchGqlSchema(endpoint, environment)
fetchGqlSchema(endpoint, environment, request, collection)
.then((res) => res.data)
.then((s) => {
if (s && s.data) {
@@ -40,7 +40,7 @@ const useGraphqlSchema = (endpoint, environment) => {
.catch((err) => {
setIsLoading(false);
setError(err);
toast.error('Error occured while loading GraphQL Schema');
toast.error('Error occurred while loading GraphQL Schema');
});
};

View File

@@ -9,9 +9,7 @@ import StyledWrapper from './StyledWrapper';
const GraphQLVariables = ({ variables, item, collection }) => {
const dispatch = useDispatch();
const {
storedTheme
} = useTheme();
const { storedTheme } = useTheme();
const onEdit = (value) => {
dispatch(
@@ -29,10 +27,11 @@ const GraphQLVariables = ({ variables, item, collection }) => {
return (
<StyledWrapper className="w-full">
<CodeEditor
collection={collection} value={variables || ''}
collection={collection}
value={variables || ''}
theme={storedTheme}
onEdit={onEdit}
mode='javascript'
mode="javascript"
onRun={onRun}
onSave={onSave}
/>

View File

@@ -7,6 +7,8 @@ import QueryParams from 'components/RequestPane/QueryParams';
import RequestHeaders from 'components/RequestPane/RequestHeaders';
import RequestBody from 'components/RequestPane/RequestBody';
import RequestBodyMode from 'components/RequestPane/RequestBody/RequestBodyMode';
import Auth from 'components/RequestPane/Auth';
import AuthMode from 'components/RequestPane/Auth/AuthMode';
import Vars from 'components/RequestPane/Vars';
import Assertions from 'components/RequestPane/Assertions';
import Script from 'components/RequestPane/Script';
@@ -38,6 +40,9 @@ const HttpRequestPane = ({ item, collection, leftPaneWidth }) => {
case 'headers': {
return <RequestHeaders item={item} collection={collection} />;
}
case 'auth': {
return <Auth item={item} collection={collection} />;
}
case 'vars': {
return <Vars item={item} collection={collection} />;
}
@@ -62,7 +67,7 @@ const HttpRequestPane = ({ item, collection, leftPaneWidth }) => {
const focusedTab = find(tabs, (t) => t.uid === activeTabUid);
if (!focusedTab || !focusedTab.uid || !focusedTab.requestPaneTab) {
return <div className="pb-4 px-4">An error occured!</div>;
return <div className="pb-4 px-4">An error occurred!</div>;
}
const getTabClassname = (tabName) => {
@@ -73,7 +78,7 @@ const HttpRequestPane = ({ item, collection, leftPaneWidth }) => {
return (
<StyledWrapper className="flex flex-col h-full relative">
<div className="flex items-center tabs" role="tablist">
<div className="flex flex-wrap items-center tabs" role="tablist">
<div className={getTabClassname('params')} role="tab" onClick={() => selectTab('params')}>
Query
</div>
@@ -83,6 +88,9 @@ const HttpRequestPane = ({ item, collection, leftPaneWidth }) => {
<div className={getTabClassname('headers')} role="tab" onClick={() => selectTab('headers')}>
Headers
</div>
<div className={getTabClassname('auth')} role="tab" onClick={() => selectTab('auth')}>
Auth
</div>
<div className={getTabClassname('vars')} role="tab" onClick={() => selectTab('vars')}>
Vars
</div>
@@ -95,15 +103,17 @@ const HttpRequestPane = ({ item, collection, leftPaneWidth }) => {
<div className={getTabClassname('tests')} role="tab" onClick={() => selectTab('tests')}>
Tests
</div>
{/* Moved to post mvp */}
{/* <div className={getTabClassname('auth')} role="tab" onClick={() => selectTab('auth')}>Auth</div> */}
{focusedTab.requestPaneTab === 'body' ? (
<div className="flex flex-grow justify-end items-center">
<RequestBodyMode item={item} collection={collection} />
</div>
) : null}
</div>
<section className={`flex w-full ${['script', 'vars'].includes(focusedTab.requestPaneTab) ? '' : 'mt-5'}`}>{getTabPanel(focusedTab.requestPaneTab)}</section>
<section
className={`flex w-full ${['script', 'vars', 'auth'].includes(focusedTab.requestPaneTab) ? '' : 'mt-5'}`}
>
{getTabPanel(focusedTab.requestPaneTab)}
</section>
</StyledWrapper>
);
};

View File

@@ -41,7 +41,6 @@ const Wrapper = styled.div`
color: ${(props) => props.theme.table.input.color};
background: transparent;
&:focus {
outline: none !important;
border: solid 1px transparent;

View File

@@ -4,7 +4,11 @@ import cloneDeep from 'lodash/cloneDeep';
import { IconTrash } from '@tabler/icons';
import { useDispatch } from 'react-redux';
import { useTheme } from 'providers/Theme';
import { addMultipartFormParam, updateMultipartFormParam, deleteMultipartFormParam } from 'providers/ReduxStore/slices/collections';
import {
addMultipartFormParam,
updateMultipartFormParam,
deleteMultipartFormParam
} from 'providers/ReduxStore/slices/collections';
import SingleLineEditor from 'components/SingleLineEditor';
import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
import StyledWrapper from './StyledWrapper';
@@ -88,23 +92,35 @@ const MultipartFormParams = ({ item, collection }) => {
/>
</td>
<td>
<SingleLineEditor
<SingleLineEditor
onSave={onSave}
theme={storedTheme}
value={param.value}
onChange={(newValue) => handleParamChange({
target: {
value: newValue
}
}, param, 'value')}
value={param.value}
onChange={(newValue) =>
handleParamChange(
{
target: {
value: newValue
}
},
param,
'value'
)
}
onRun={handleRun}
collection={collection}
/>
</td>
<td>
<div className="flex items-center">
<input type="checkbox" checked={param.enabled} className="mr-3 mousetrap" onChange={(e) => handleParamChange(e, param, 'enabled')} />
<button onClick={() => handleRemoveParams(param)}>
<input
type="checkbox"
checked={param.enabled}
tabIndex="-1"
className="mr-3 mousetrap"
onChange={(e) => handleParamChange(e, param, 'enabled')}
/>
<button tabIndex="-1" onClick={() => handleRemoveParams(param)}>
<IconTrash strokeWidth={1.5} size={20} />
</button>
</div>

View File

@@ -15,16 +15,19 @@ const StyledWrapper = styled.div`
// Todo: dark mode temporary fix
// Clean this
.CodeMirror.cm-s-monokai {
.CodeMirror-overlayscroll-horizontal div, .CodeMirror-overlayscroll-vertical div {
.CodeMirror-overlayscroll-horizontal div,
.CodeMirror-overlayscroll-vertical div {
background: #444444;
}
}
.cm-s-monokai span.cm-property, .cm-s-monokai span.cm-attribute {
.cm-s-monokai span.cm-property,
.cm-s-monokai span.cm-attribute {
color: #9cdcfe !important;
}
.cm-s-monokai span.cm-property, .cm-s-monokai span.cm-attribute {
.cm-s-monokai span.cm-property,
.cm-s-monokai span.cm-attribute {
color: #9cdcfe !important;
}
@@ -32,16 +35,20 @@ const StyledWrapper = styled.div`
color: #ce9178 !important;
}
.cm-s-monokai span.cm-number{
.cm-s-monokai span.cm-number {
color: #b5cea8 !important;
}
.cm-s-monokai span.cm-atom{
.cm-s-monokai span.cm-atom {
color: #569cd6 !important;
}
.cm-variable-valid{color: green}
.cm-variable-invalid{color: red}
.cm-variable-valid {
color: green;
}
.cm-variable-invalid {
color: red;
}
`;
export default StyledWrapper;

View File

@@ -43,7 +43,7 @@ export default class QueryEditor extends React.Component {
mode: 'graphql',
// mode: 'brunovariables',
brunoVarInfo: {
variables: getAllVariables(this.props.collection),
variables: getAllVariables(this.props.collection)
},
theme: this.props.editorTheme || 'graphiql',
theme: this.props.theme === 'dark' ? 'monokai' : 'default',
@@ -51,7 +51,7 @@ export default class QueryEditor extends React.Component {
autoCloseBrackets: true,
matchBrackets: true,
showCursorWhenSelecting: true,
scrollbarStyle: "overlay",
scrollbarStyle: 'overlay',
readOnly: this.props.readOnly ? 'nocursor' : false,
foldGutter: {
minFoldSize: 4
@@ -142,7 +142,7 @@ export default class QueryEditor extends React.Component {
}
componentDidUpdate(prevProps) {
// Ensure the changes caused by this update are not interpretted as
// Ensure the changes caused by this update are not interpreted as
// user-input changes which could otherwise result in an infinite
// event loop.
this.ignoreChangeEvent = true;
@@ -179,14 +179,13 @@ export default class QueryEditor extends React.Component {
}
// Todo: Overlay is messing up with schema hint
// Fix this
// Fix this
addOverlay = () => {
// let variables = getAllVariables(this.props.collection);
// this.variables = variables;
// defineCodeMirrorBrunoVariablesMode(variables, 'graphql');
// this.editor.setOption('mode', 'brunovariables');
}
};
render() {
return (

View File

@@ -59,7 +59,10 @@ export default function onHasCompletion(_cm, data, onHintInformationRender) {
const description = ctx.description ? md.render(ctx.description) : 'Self descriptive.';
const type = ctx.type ? '<span className="infoType">' + renderType(ctx.type) + '</span>' : '';
information.innerHTML = '<div className="content">' + (description.slice(0, 3) === '<p>' ? '<p>' + type + description.slice(3) : type + description) + '</div>';
information.innerHTML =
'<div className="content">' +
(description.slice(0, 3) === '<p>' ? '<p>' + type + description.slice(3) : type + description) +
'</div>';
if (ctx && deprecation && ctx.deprecationReason) {
const reason = ctx.deprecationReason ? md.render(ctx.deprecationReason) : '';

View File

@@ -13,7 +13,7 @@ const Wrapper = styled.div`
}
thead {
color: ${(props) => props.theme.table.thead.color};;
color: ${(props) => props.theme.table.thead.color};
font-size: 0.8125rem;
user-select: none;
}

View File

@@ -91,23 +91,35 @@ const QueryParams = ({ item, collection }) => {
/>
</td>
<td>
<SingleLineEditor
<SingleLineEditor
value={param.value}
theme={storedTheme}
onSave={onSave}
onChange={(newValue) => handleParamChange({
target: {
value: newValue
}
}, param, 'value')}
onChange={(newValue) =>
handleParamChange(
{
target: {
value: newValue
}
},
param,
'value'
)
}
onRun={handleRun}
collection={collection}
/>
</td>
<td>
<div className="flex items-center">
<input type="checkbox" checked={param.enabled} className="mr-3 mousetrap" onChange={(e) => handleParamChange(e, param, 'enabled')} />
<button onClick={() => handleRemoveParam(param)}>
<input
type="checkbox"
checked={param.enabled}
tabIndex="-1"
className="mr-3 mousetrap"
onChange={(e) => handleParamChange(e, param, 'enabled')}
/>
<button tabIndex="-1" onClick={() => handleRemoveParam(param)}>
<IconTrash strokeWidth={1.5} size={20} />
</button>
</div>

View File

@@ -10,7 +10,9 @@ const HttpMethodSelector = ({ method, onMethodSelect }) => {
const Icon = forwardRef((props, ref) => {
return (
<div ref={ref} className="flex w-full items-center pl-3 py-1 select-none uppercase">
<div className="flex-grow font-medium" id="create-new-request-method">{method}</div>
<div className="flex-grow font-medium" id="create-new-request-method">
{method}
</div>
<div>
<IconCaretDown className="caret ml-2 mr-2" size={14} strokeWidth={2} />
</div>

View File

@@ -32,6 +32,50 @@ const Wrapper = styled.div`
position: relative;
top: 1px;
}
.tooltip {
position: relative;
display: inline-block;
cursor: pointer;
}
.tooltip:hover .tooltiptext {
visibility: visible;
opacity: 1;
}
.tooltiptext {
visibility: hidden;
width: auto;
background-color: ${(props) => props.theme.requestTabs.active.bg};
color: ${(props) => props.theme.text};
text-align: center;
border-radius: 4px;
padding: 4px 8px;
position: absolute;
z-index: 1;
bottom: 34px;
left: 50%;
transform: translateX(-50%);
opacity: 0;
transition: opacity 0.3s;
white-space: nowrap;
}
.tooltiptext::after {
content: '';
position: absolute;
top: 100%;
left: 50%;
margin-left: -4px;
border-width: 4px;
border-style: solid;
border-color: ${(props) => props.theme.requestTabs.active.bg} transparent transparent transparent;
}
.shortcut {
font-size: 0.625rem;
}
`;
export default Wrapper;

View File

@@ -1,12 +1,13 @@
import React, { useState, useEffect} from 'react';
import React, { useState, useEffect } from 'react';
import get from 'lodash/get';
import { useDispatch } from 'react-redux';
import { requestUrlChanged, updateRequestMethod } from 'providers/ReduxStore/slices/collections';
import { saveRequest } from 'providers/ReduxStore/slices/collections/actions';
import HttpMethodSelector from './HttpMethodSelector';
import { useTheme } from 'providers/Theme';
import SendIcon from 'components/Icons/Send';
import { IconDeviceFloppy, IconArrowRight } from '@tabler/icons';
import SingleLineEditor from 'components/SingleLineEditor';
import { isMacOS } from 'utils/common/platform';
import StyledWrapper from './StyledWrapper';
const QueryUrl = ({ item, collection, handleRun }) => {
@@ -14,15 +15,20 @@ const QueryUrl = ({ item, collection, handleRun }) => {
const dispatch = useDispatch();
const method = item.draft ? get(item, 'draft.request.method') : get(item, 'request.method');
const url = item.draft ? get(item, 'draft.request.url') : get(item, 'request.url');
const isMac = isMacOS();
const saveShortcut = isMac ? 'Cmd + S' : 'Ctrl + S';
const [methodSelectorWidth, setMethodSelectorWidth] = useState(90);
useEffect(() => {
const el = document.querySelector(".method-selector-container");
const el = document.querySelector('.method-selector-container');
setMethodSelectorWidth(el.offsetWidth);
}, [method]);
const onSave = () => dispatch(saveRequest(item.uid, collection.uid));
const onSave = () => {
dispatch(saveRequest(item.uid, collection.uid));
};
const onUrlChange = (value) => {
dispatch(
requestUrlChanged({
@@ -56,8 +62,8 @@ const QueryUrl = ({ item, collection, handleRun }) => {
maxWidth: `calc(100% - ${methodSelectorWidth}px)`
}}
>
<SingleLineEditor
value={url}
<SingleLineEditor
value={url}
onSave={onSave}
theme={storedTheme}
onChange={(newValue) => onUrlChange(newValue)}
@@ -65,7 +71,25 @@ const QueryUrl = ({ item, collection, handleRun }) => {
collection={collection}
/>
<div className="flex items-center h-full mr-2 cursor-pointer" id="send-request" onClick={handleRun}>
<SendIcon color={theme.requestTabPanel.url.icon} width={22}/>
<div
className="tooltip mr-3"
onClick={(e) => {
e.stopPropagation();
if (!item.draft) return;
onSave();
}}
>
<IconDeviceFloppy
color={item.draft ? theme.colors.text.yellow : theme.requestTabs.icon.color}
strokeWidth={1.5}
size={22}
className={`${item.draft ? 'cursor-pointer' : 'cursor-default'}`}
/>
<span className="tooltiptext text-xs">
Save <span className="shortcut">({saveShortcut})</span>
</span>
</div>
<IconArrowRight color={theme.requestTabPanel.url.icon} strokeWidth={1.5} size={22} />
</div>
</div>
</StyledWrapper>

View File

@@ -13,9 +13,7 @@ const RequestBody = ({ item, collection }) => {
const dispatch = useDispatch();
const body = item.draft ? get(item, 'draft.request.body') : get(item, 'request.body');
const bodyMode = item.draft ? get(item, 'draft.request.body.mode') : get(item, 'request.body.mode');
const {
storedTheme
} = useTheme();
const { storedTheme } = useTheme();
const onEdit = (value) => {
dispatch(
@@ -45,7 +43,15 @@ const RequestBody = ({ item, collection }) => {
return (
<StyledWrapper className="w-full">
<CodeEditor collection={collection} theme={storedTheme} value={bodyContent[bodyMode] || ''} onEdit={onEdit} onRun={onRun} onSave={onSave} mode={codeMirrorMode[bodyMode]} />
<CodeEditor
collection={collection}
theme={storedTheme}
value={bodyContent[bodyMode] || ''}
onEdit={onEdit}
onRun={onRun}
onSave={onSave}
mode={codeMirrorMode[bodyMode]}
/>
</StyledWrapper>
);
}

View File

@@ -8,6 +8,8 @@ import { addRequestHeader, updateRequestHeader, deleteRequestHeader } from 'prov
import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
import SingleLineEditor from 'components/SingleLineEditor';
import StyledWrapper from './StyledWrapper';
import { headers as StandardHTTPHeaders } from 'know-your-http-well';
const headerAutoCompleteList = StandardHTTPHeaders.map((e) => e.header);
const RequestHeaders = ({ item, collection }) => {
const dispatch = useDispatch();
@@ -72,19 +74,28 @@ const RequestHeaders = ({ item, collection }) => {
</thead>
<tbody>
{headers && headers.length
? headers.map((header, index) => {
? headers.map((header) => {
return (
<tr key={header.uid}>
<td>
<input
type="text"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
<SingleLineEditor
value={header.name}
className="mousetrap"
onChange={(e) => handleHeaderValueChange(e, header, 'name')}
theme={storedTheme}
onSave={onSave}
onChange={(newValue) =>
handleHeaderValueChange(
{
target: {
value: newValue
}
},
header,
'name'
)
}
autocomplete={headerAutoCompleteList}
onRun={handleRun}
collection={collection}
/>
</td>
<td>
@@ -92,19 +103,31 @@ const RequestHeaders = ({ item, collection }) => {
value={header.value}
theme={storedTheme}
onSave={onSave}
onChange={(newValue) => handleHeaderValueChange({
target: {
value: newValue
}
}, header, 'value')}
onChange={(newValue) =>
handleHeaderValueChange(
{
target: {
value: newValue
}
},
header,
'value'
)
}
onRun={handleRun}
collection={collection}
/>
</td>
<td>
<div className="flex items-center">
<input type="checkbox" checked={header.enabled} className="mr-3 mousetrap" onChange={(e) => handleHeaderValueChange(e, header, 'enabled')} />
<button onClick={() => handleRemoveHeader(header)}>
<input
type="checkbox"
checked={header.enabled}
tabIndex="-1"
className="mr-3 mousetrap"
onChange={(e) => handleHeaderValueChange(e, header, 'enabled')}
/>
<button tabIndex="-1" onClick={() => handleRemoveHeader(header)}>
<IconTrash strokeWidth={1.5} size={20} />
</button>
</div>

View File

@@ -28,7 +28,14 @@ const SaveRequest = ({ items, onClose }) => {
return (
<StyledWrapper>
<Modal size="md" title="Save Request" confirmText="Save" cancelText="Cancel" handleCancel={onClose} handleConfirm={onClose}>
<Modal
size="md"
title="Save Request"
confirmText="Save"
cancelText="Cancel"
handleCancel={onClose}
handleConfirm={onClose}
>
<p className="mb-2">Select a folder to save request:</p>
<div className="folder-list">
{showFolders && showFolders.length

View File

@@ -12,9 +12,7 @@ const Script = ({ item, collection }) => {
const requestScript = item.draft ? get(item, 'draft.request.script.req') : get(item, 'request.script.req');
const responseScript = item.draft ? get(item, 'draft.request.script.res') : get(item, 'request.script.res');
const {
storedTheme
} = useTheme();
const { storedTheme } = useTheme();
const onRequestScriptEdit = (value) => {
dispatch(
@@ -29,36 +27,38 @@ const Script = ({ item, collection }) => {
const onResponseScriptEdit = (value) => {
dispatch(
updateResponseScript({
script: value,
itemUid: item.uid,
collectionUid: collection.uid
})
);
};
script: value,
itemUid: item.uid,
collectionUid: collection.uid
})
);
};
const onRun = () => dispatch(sendRequest(item, collection.uid));
const onSave = () => dispatch(saveRequest(item.uid, collection.uid));
return (
<StyledWrapper className="w-full flex flex-col">
<div className='flex-1 mt-2'>
<div className='mb-1 title text-xs'>Pre Request</div>
<div className="flex-1 mt-2">
<div className="mb-1 title text-xs">Pre Request</div>
<CodeEditor
collection={collection} value={requestScript || ''}
collection={collection}
value={requestScript || ''}
theme={storedTheme}
onEdit={onRequestScriptEdit}
mode='javascript'
mode="javascript"
onRun={onRun}
onSave={onSave}
/>
</div>
<div className='flex-1 mt-6'>
<div className='mt-1 mb-1 title text-xs'>Post Response</div>
<div className="flex-1 mt-6">
<div className="mt-1 mb-1 title text-xs">Post Response</div>
<CodeEditor
collection={collection} value={responseScript || ''}
collection={collection}
value={responseScript || ''}
theme={storedTheme}
onEdit={onResponseScriptEdit}
mode='javascript'
mode="javascript"
onRun={onRun}
onSave={onSave}
/>

View File

@@ -11,9 +11,7 @@ const Tests = ({ item, collection }) => {
const dispatch = useDispatch();
const tests = item.draft ? get(item, 'draft.request.tests') : get(item, 'request.tests');
const {
storedTheme
} = useTheme();
const { storedTheme } = useTheme();
const onEdit = (value) => {
dispatch(
@@ -31,10 +29,11 @@ const Tests = ({ item, collection }) => {
return (
<StyledWrapper className="w-full">
<CodeEditor
collection={collection} value={tests || ''}
collection={collection}
value={tests || ''}
theme={storedTheme}
onEdit={onEdit}
mode='javascript'
mode="javascript"
onRun={onRun}
onSave={onSave}
/>

View File

@@ -68,18 +68,18 @@ const VarsTable = ({ item, collection, vars, varType }) => {
<thead>
<tr>
<td>Name</td>
{ varType === 'request' ? (
{varType === 'request' ? (
<td>
<div className='flex items-center'>
<div className="flex items-center">
<span>Value</span>
<Tooltip text="You can write any valid JS Template Literal here" tooltipId="request-var"/>
<Tooltip text="You can write any valid JS Template Literal here" tooltipId="request-var" />
</div>
</td>
) : (
) : (
<td>
<div className='flex items-center'>
<div className="flex items-center">
<span>Expr</span>
<Tooltip text="You can write any valid JS expression here" tooltipId="response-var"/>
<Tooltip text="You can write any valid JS expression here" tooltipId="response-var" />
</div>
</td>
)}
@@ -108,11 +108,17 @@ const VarsTable = ({ item, collection, vars, varType }) => {
value={_var.value}
theme={storedTheme}
onSave={onSave}
onChange={(newValue) => handleVarChange({
target: {
value: newValue
}
}, _var, 'value')}
onChange={(newValue) =>
handleVarChange(
{
target: {
value: newValue
}
},
_var,
'value'
)
}
onRun={handleRun}
collection={collection}
/>
@@ -122,10 +128,11 @@ const VarsTable = ({ item, collection, vars, varType }) => {
<input
type="checkbox"
checked={_var.enabled}
tabIndex="-1"
className="mr-3 mousetrap"
onChange={(e) => handleVarChange(e, _var, 'enabled')}
/>
<button onClick={() => handleRemoveVar(_var)}>
<button tabIndex="-1" onClick={() => handleRemoveVar(_var)}>
<IconTrash strokeWidth={1.5} size={20} />
</button>
</div>

View File

@@ -12,9 +12,7 @@ const Vars = ({ item, collection }) => {
const requestVars = item.draft ? get(item, 'draft.request.vars.req') : get(item, 'request.vars.req');
const responseVars = item.draft ? get(item, 'draft.request.vars.res') : get(item, 'request.vars.res');
const {
storedTheme
} = useTheme();
const { storedTheme } = useTheme();
const onRequestScriptEdit = (value) => {
dispatch(
@@ -29,25 +27,25 @@ const Vars = ({ item, collection }) => {
const onResponseScriptEdit = (value) => {
dispatch(
updateResponseScript({
script: value,
itemUid: item.uid,
collectionUid: collection.uid
})
);
};
script: value,
itemUid: item.uid,
collectionUid: collection.uid
})
);
};
const onRun = () => dispatch(sendRequest(item, collection.uid));
const onSave = () => dispatch(saveRequest(item.uid, collection.uid));
return (
<StyledWrapper className="w-full flex flex-col">
<div className='flex-1 mt-2'>
<div className='mb-1 title text-xs'>Pre Request</div>
<VarsTable item={item} collection={collection} vars={requestVars} varType='request'/>
<div className="flex-1 mt-2">
<div className="mb-1 title text-xs">Pre Request</div>
<VarsTable item={item} collection={collection} vars={requestVars} varType="request" />
</div>
<div className='flex-1'>
<div className='mt-1 mb-1 title text-xs'>Post Response</div>
<VarsTable item={item} collection={collection} vars={responseVars} varType='response'/>
<div className="flex-1">
<div className="mt-1 mb-1 title text-xs">Post Response</div>
<VarsTable item={item} collection={collection} vars={responseVars} varType="response" />
</div>
</StyledWrapper>
);

View File

@@ -24,7 +24,7 @@ const RequestNotFound = ({ itemUid }) => {
// and then shows the error message after a delay
// this will prevent the error message from flashing on the screen
if(!showErrorMessage) {
if (!showErrorMessage) {
return null;
}
@@ -32,7 +32,9 @@ const RequestNotFound = ({ itemUid }) => {
<div className="mt-6 px-6">
<div className="p-4 bg-orange-100 border-l-4 border-yellow-500 text-yellow-700 bg-yellow-100 p-4">
<div>Request no longer exists.</div>
<div className="mt-2">This can happen when the .bru file associated with this request was deleted on your filesystem.</div>
<div className="mt-2">
This can happen when the .bru file associated with this request was deleted on your filesystem.
</div>
</div>
<button className="btn btn-md btn-secondary mt-6" onClick={closeTab}>
Close Tab

View File

@@ -13,6 +13,8 @@ import RequestNotFound from './RequestNotFound';
import QueryUrl from 'components/RequestPane/QueryUrl';
import NetworkError from 'components/ResponsePane/NetworkError';
import RunnerResults from 'components/RunnerResults';
import VariablesEditor from 'components/VariablesEditor';
import CollectionSettings from 'components/CollectionSettings';
import { DocExplorer } from '@usebruno/graphql-docs';
import StyledWrapper from './StyledWrapper';
@@ -33,7 +35,9 @@ const RequestTabPanel = () => {
let asideWidth = useSelector((state) => state.app.leftSidebarWidth);
const focusedTab = find(tabs, (t) => t.uid === activeTabUid);
const [leftPaneWidth, setLeftPaneWidth] = useState(focusedTab && focusedTab.requestPaneWidth ? focusedTab.requestPaneWidth : (screenWidth - asideWidth) / 2.2); // 2.2 so that request pane is relatively smaller
const [leftPaneWidth, setLeftPaneWidth] = useState(
focusedTab && focusedTab.requestPaneWidth ? focusedTab.requestPaneWidth : (screenWidth - asideWidth) / 2.2
); // 2.2 so that request pane is relatively smaller
const [rightPaneWidth, setRightPaneWidth] = useState(screenWidth - asideWidth - leftPaneWidth - DEFAULT_PADDING);
const [dragging, setDragging] = useState(false);
@@ -45,10 +49,10 @@ const RequestTabPanel = () => {
const onSchemaLoad = (schema) => setSchema(schema);
const toggleDocs = () => setShowGqlDocs((showGqlDocs) => !showGqlDocs);
const handleGqlClickReference = (reference) => {
if(docExplorerRef.current) {
if (docExplorerRef.current) {
docExplorerRef.current.showDocForReference(reference);
}
if(!showGqlDocs) {
if (!showGqlDocs) {
setShowGqlDocs(true);
}
};
@@ -66,10 +70,13 @@ const RequestTabPanel = () => {
if (dragging) {
e.preventDefault();
let leftPaneXPosition = e.clientX + 2;
if (leftPaneXPosition < (asideWidth+ DEFAULT_PADDING + MIN_LEFT_PANE_WIDTH) || leftPaneXPosition > (screenWidth - MIN_RIGHT_PANE_WIDTH )) {
if (
leftPaneXPosition < asideWidth + DEFAULT_PADDING + MIN_LEFT_PANE_WIDTH ||
leftPaneXPosition > screenWidth - MIN_RIGHT_PANE_WIDTH
) {
return;
}
setLeftPaneWidth(leftPaneXPosition- asideWidth);
setLeftPaneWidth(leftPaneXPosition - asideWidth);
setRightPaneWidth(screenWidth - e.clientX - DEFAULT_PADDING);
}
};
@@ -105,7 +112,7 @@ const RequestTabPanel = () => {
}
if (!focusedTab || !focusedTab.uid || !focusedTab.collectionUid) {
return <div className="pb-4 px-4">An error occured!</div>;
return <div className="pb-4 px-4">An error occurred!</div>;
}
let collection = find(collections, (c) => c.uid === focusedTab.collectionUid);
@@ -113,9 +120,16 @@ const RequestTabPanel = () => {
return <div className="pb-4 px-4">Collection not found!</div>;
}
const showRunner = collection.showRunner;
if(showRunner) {
return <RunnerResults collection={collection}/>;
if (focusedTab.type === 'collection-runner') {
return <RunnerResults collection={collection} />;
}
if (focusedTab.type === 'variables') {
return <VariablesEditor collection={collection} />;
}
if (focusedTab.type === 'collection-settings') {
return <CollectionSettings collection={collection} />;
}
const item = findItemInCollection(collection, activeTabUid);
@@ -138,7 +152,13 @@ const RequestTabPanel = () => {
</div>
<section className="main flex flex-grow pb-4 relative">
<section className="request-pane">
<div className="px-4" style={{ width: `${Math.max(leftPaneWidth, MIN_LEFT_PANE_WIDTH)}px`, height: `calc(100% - ${DEFAULT_PADDING}px)` }}>
<div
className="px-4"
style={{
width: `${Math.max(leftPaneWidth, MIN_LEFT_PANE_WIDTH)}px`,
height: `calc(100% - ${DEFAULT_PADDING}px)`
}}
>
{item.type === 'graphql-request' ? (
<GraphQLRequestPane
item={item}
@@ -150,7 +170,9 @@ const RequestTabPanel = () => {
/>
) : null}
{item.type === 'http-request' ? <HttpRequestPane item={item} collection={collection} leftPaneWidth={leftPaneWidth} /> : null}
{item.type === 'http-request' ? (
<HttpRequestPane item={item} collection={collection} leftPaneWidth={leftPaneWidth} />
) : null}
</div>
</section>
@@ -165,19 +187,15 @@ const RequestTabPanel = () => {
{item.type === 'graphql-request' ? (
<div className={`graphql-docs-explorer-container ${showGqlDocs ? '' : 'hidden'}`}>
<DocExplorer schema={schema} ref={(r) => docExplorerRef.current = r}>
<button
className='mr-2'
onClick={toggleDocs}
aria-label="Close Documentation Explorer"
>
<DocExplorer schema={schema} ref={(r) => (docExplorerRef.current = r)}>
<button className="mr-2" onClick={toggleDocs} aria-label="Close Documentation Explorer">
{'\u2715'}
</button>
</DocExplorer>
</div>
): null}
) : null}
</StyledWrapper>
);
};
export default RequestTabPanel;
export default RequestTabPanel;

View File

@@ -1,18 +1,42 @@
import React from 'react';
import { IconFiles, IconRun } from '@tabler/icons';
import { uuid } from 'utils/common';
import { IconFiles, IconRun, IconEye, IconSettings } from '@tabler/icons';
import EnvironmentSelector from 'components/Environments/EnvironmentSelector';
import VariablesView from 'components/VariablesView';
import { addTab } from 'providers/ReduxStore/slices/tabs';
import { useDispatch } from 'react-redux';
import { toggleRunnerView } from 'providers/ReduxStore/slices/collections';
import StyledWrapper from './StyledWrapper';
const CollectionToolBar = ({ collection }) => {
const dispatch = useDispatch();
const handleRun = () => {
dispatch(toggleRunnerView({
collectionUid: collection.uid
}));
dispatch(
addTab({
uid: uuid(),
collectionUid: collection.uid,
type: 'collection-runner'
})
);
};
const viewVariables = () => {
dispatch(
addTab({
uid: uuid(),
collectionUid: collection.uid,
type: 'variables'
})
);
};
const viewCollectionSettings = () => {
dispatch(
addTab({
uid: uuid(),
collectionUid: collection.uid,
type: 'collection-settings'
})
);
};
return (
@@ -26,7 +50,12 @@ const CollectionToolBar = ({ collection }) => {
<span className="mr-2">
<IconRun className="cursor-pointer" size={20} strokeWidth={1.5} onClick={handleRun} />
</span>
<VariablesView collection={collection}/>
<span className="mr-3">
<IconEye className="cursor-pointer" size={18} strokeWidth={1.5} onClick={viewVariables} />
</span>
<span className="mr-3">
<IconSettings className="cursor-pointer" size={18} strokeWidth={1.5} onClick={viewCollectionSettings} />
</span>
<EnvironmentSelector collection={collection} />
</div>
</div>

View File

@@ -0,0 +1,30 @@
import React from 'react';
import Modal from 'components/Modal';
const ConfirmRequestClose = ({ onCancel, onCloseWithoutSave, onSaveAndClose }) => {
const _handleCancel = ({ type }) => {
if (type === 'button') {
return onCloseWithoutSave();
}
return onCancel();
};
return (
<Modal
size="sm"
title="Unsaved changes"
confirmText="Save and Close"
cancelText="Close without saving"
handleConfirm={onSaveAndClose}
handleCancel={_handleCancel}
disableEscapeKey={true}
disableCloseOnOutsideClick={true}
closeModalFadeTimeout={150}
>
<div className="font-normal">You have unsaved changes in you request.</div>
</Modal>
);
};
export default ConfirmRequestClose;

Some files were not shown because too many files have changed in this diff Show More