mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-11 09:51:30 +00:00
fix(ui): correct “modified” indicator state across collection, folder, request, and presets/auth tabs (#3386) (#8027)
* fix: 3296 Folder-level No Auth inheritance is ignored; requests still use Collection Auth
This commit is contained in:
204
package-lock.json
generated
204
package-lock.json
generated
@@ -5027,7 +5027,7 @@
|
||||
"version": "7.26.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.26.3.tgz",
|
||||
"integrity": "sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-annotate-as-pure": "^7.25.9",
|
||||
@@ -5045,7 +5045,7 @@
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz",
|
||||
"integrity": "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-compilation-targets": "^7.22.6",
|
||||
@@ -5062,7 +5062,7 @@
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
@@ -5080,7 +5080,7 @@
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@babel/helper-globals": {
|
||||
@@ -5160,7 +5160,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz",
|
||||
"integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-annotate-as-pure": "^7.25.9",
|
||||
@@ -5235,7 +5235,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz",
|
||||
"integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.25.9",
|
||||
@@ -5278,7 +5278,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz",
|
||||
"integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9",
|
||||
@@ -5295,7 +5295,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz",
|
||||
"integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -5311,7 +5311,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz",
|
||||
"integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -5327,7 +5327,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz",
|
||||
"integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9",
|
||||
@@ -5345,7 +5345,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz",
|
||||
"integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9",
|
||||
@@ -5380,7 +5380,7 @@
|
||||
"version": "7.21.0-placeholder-for-preset-env.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
|
||||
"integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -5479,7 +5479,7 @@
|
||||
"version": "7.26.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz",
|
||||
"integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -5495,7 +5495,7 @@
|
||||
"version": "7.26.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz",
|
||||
"integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -5677,7 +5677,7 @@
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz",
|
||||
"integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.18.6",
|
||||
@@ -5694,7 +5694,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz",
|
||||
"integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -5710,7 +5710,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz",
|
||||
"integrity": "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9",
|
||||
@@ -5728,7 +5728,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz",
|
||||
"integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-module-imports": "^7.25.9",
|
||||
@@ -5746,7 +5746,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz",
|
||||
"integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -5762,7 +5762,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz",
|
||||
"integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -5794,7 +5794,7 @@
|
||||
"version": "7.26.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz",
|
||||
"integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-create-class-features-plugin": "^7.25.9",
|
||||
@@ -5811,7 +5811,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz",
|
||||
"integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-annotate-as-pure": "^7.25.9",
|
||||
@@ -5832,7 +5832,7 @@
|
||||
"version": "11.12.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
@@ -5842,7 +5842,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz",
|
||||
"integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9",
|
||||
@@ -5859,7 +5859,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz",
|
||||
"integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -5875,7 +5875,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz",
|
||||
"integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
|
||||
@@ -5892,7 +5892,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz",
|
||||
"integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -5908,7 +5908,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz",
|
||||
"integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
|
||||
@@ -5925,7 +5925,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz",
|
||||
"integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -5941,7 +5941,7 @@
|
||||
"version": "7.26.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz",
|
||||
"integrity": "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -5957,7 +5957,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz",
|
||||
"integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -5989,7 +5989,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz",
|
||||
"integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9",
|
||||
@@ -6006,7 +6006,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz",
|
||||
"integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-compilation-targets": "^7.25.9",
|
||||
@@ -6024,7 +6024,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz",
|
||||
"integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -6040,7 +6040,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz",
|
||||
"integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -6056,7 +6056,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz",
|
||||
"integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -6072,7 +6072,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz",
|
||||
"integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -6088,7 +6088,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz",
|
||||
"integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-module-transforms": "^7.25.9",
|
||||
@@ -6121,7 +6121,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz",
|
||||
"integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-module-transforms": "^7.25.9",
|
||||
@@ -6140,7 +6140,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz",
|
||||
"integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-module-transforms": "^7.25.9",
|
||||
@@ -6157,7 +6157,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz",
|
||||
"integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
|
||||
@@ -6174,7 +6174,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz",
|
||||
"integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -6205,7 +6205,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz",
|
||||
"integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -6221,7 +6221,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz",
|
||||
"integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-compilation-targets": "^7.25.9",
|
||||
@@ -6239,7 +6239,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz",
|
||||
"integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9",
|
||||
@@ -6256,7 +6256,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz",
|
||||
"integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -6288,7 +6288,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz",
|
||||
"integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -6320,7 +6320,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz",
|
||||
"integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-annotate-as-pure": "^7.25.9",
|
||||
@@ -6338,7 +6338,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz",
|
||||
"integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -6423,7 +6423,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz",
|
||||
"integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9",
|
||||
@@ -6440,7 +6440,7 @@
|
||||
"version": "7.26.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz",
|
||||
"integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
|
||||
@@ -6457,7 +6457,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz",
|
||||
"integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -6473,7 +6473,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz",
|
||||
"integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -6489,7 +6489,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz",
|
||||
"integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9",
|
||||
@@ -6506,7 +6506,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz",
|
||||
"integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -6522,7 +6522,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz",
|
||||
"integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -6538,7 +6538,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz",
|
||||
"integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -6573,7 +6573,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz",
|
||||
"integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -6589,7 +6589,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz",
|
||||
"integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
|
||||
@@ -6606,7 +6606,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz",
|
||||
"integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
|
||||
@@ -6623,7 +6623,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz",
|
||||
"integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
|
||||
@@ -6640,7 +6640,7 @@
|
||||
"version": "7.26.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz",
|
||||
"integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/compat-data": "^7.26.0",
|
||||
@@ -6741,7 +6741,7 @@
|
||||
"version": "0.1.6-no-external-plugins",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz",
|
||||
"integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.0.0",
|
||||
@@ -11111,8 +11111,7 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/binding-darwin-x64": {
|
||||
"version": "1.1.8",
|
||||
@@ -11126,8 +11125,7 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/binding-linux-arm64-gnu": {
|
||||
"version": "1.1.8",
|
||||
@@ -11141,8 +11139,7 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/binding-linux-arm64-musl": {
|
||||
"version": "1.1.8",
|
||||
@@ -11156,8 +11153,7 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/binding-linux-x64-gnu": {
|
||||
"version": "1.1.8",
|
||||
@@ -11171,8 +11167,7 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/binding-linux-x64-musl": {
|
||||
"version": "1.1.8",
|
||||
@@ -11186,8 +11181,7 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/binding-win32-arm64-msvc": {
|
||||
"version": "1.1.8",
|
||||
@@ -11201,8 +11195,7 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/binding-win32-ia32-msvc": {
|
||||
"version": "1.1.8",
|
||||
@@ -11216,8 +11209,7 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/binding-win32-x64-msvc": {
|
||||
"version": "1.1.8",
|
||||
@@ -11231,8 +11223,7 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/core": {
|
||||
"version": "1.1.8",
|
||||
@@ -12562,6 +12553,7 @@
|
||||
"version": "10.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz",
|
||||
"integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.10.4",
|
||||
@@ -12581,6 +12573,7 @@
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
|
||||
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
@@ -12593,6 +12586,7 @@
|
||||
"version": "27.5.1",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
|
||||
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1",
|
||||
@@ -12607,6 +12601,7 @@
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@testing-library/jest-dom": {
|
||||
@@ -12687,6 +12682,7 @@
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
|
||||
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/babel__core": {
|
||||
@@ -12978,6 +12974,7 @@
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
|
||||
"integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/lodash": {
|
||||
@@ -13000,6 +12997,7 @@
|
||||
"version": "12.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz",
|
||||
"integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/linkify-it": "*",
|
||||
@@ -13010,6 +13008,7 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
|
||||
"integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/ms": {
|
||||
@@ -14501,6 +14500,7 @@
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
|
||||
"integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"dequal": "^2.0.3"
|
||||
@@ -14926,7 +14926,7 @@
|
||||
"version": "0.4.12",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz",
|
||||
"integrity": "sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/compat-data": "^7.22.6",
|
||||
@@ -14941,7 +14941,7 @@
|
||||
"version": "0.10.6",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz",
|
||||
"integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-define-polyfill-provider": "^0.6.2",
|
||||
@@ -14955,7 +14955,7 @@
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.3.tgz",
|
||||
"integrity": "sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-define-polyfill-provider": "^0.6.3"
|
||||
@@ -16812,7 +16812,7 @@
|
||||
"version": "3.39.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz",
|
||||
"integrity": "sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"browserslist": "^4.24.2"
|
||||
@@ -18042,6 +18042,7 @@
|
||||
"version": "0.5.16",
|
||||
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
|
||||
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dom-converter": {
|
||||
@@ -21615,7 +21616,7 @@
|
||||
"version": "2.16.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
|
||||
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hasown": "^2.0.2"
|
||||
@@ -24030,6 +24031,7 @@
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
|
||||
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"lz-string": "bin/bin.js"
|
||||
@@ -25654,7 +25656,7 @@
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/path-scurry": {
|
||||
@@ -28047,14 +28049,14 @@
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
|
||||
"integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/regenerate-unicode-properties": {
|
||||
"version": "10.2.0",
|
||||
"resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz",
|
||||
"integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"regenerate": "^1.4.2"
|
||||
@@ -28067,7 +28069,7 @@
|
||||
"version": "0.15.2",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz",
|
||||
"integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.8.4"
|
||||
@@ -28077,7 +28079,7 @@
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz",
|
||||
"integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"regenerate": "^1.4.2",
|
||||
@@ -28095,14 +28097,14 @@
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz",
|
||||
"integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/regjsparser": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz",
|
||||
"integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"jsesc": "~3.0.2"
|
||||
@@ -28115,7 +28117,7 @@
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz",
|
||||
"integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"jsesc": "bin/jsesc"
|
||||
@@ -28307,7 +28309,7 @@
|
||||
"version": "1.22.10",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
||||
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-core-module": "^2.16.0",
|
||||
@@ -30565,7 +30567,7 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@@ -31759,7 +31761,7 @@
|
||||
"version": "4.9.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
@@ -31820,7 +31822,7 @@
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz",
|
||||
"integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
@@ -31830,7 +31832,7 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
|
||||
"integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"unicode-canonical-property-names-ecmascript": "^2.0.0",
|
||||
@@ -31844,7 +31846,7 @@
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz",
|
||||
"integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
@@ -31854,7 +31856,7 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz",
|
||||
"integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
|
||||
@@ -75,13 +75,13 @@ const AuthMode = ({ collection }) => {
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<div className="inline-flex items-center cursor-pointer auth-mode-selector">
|
||||
<div className="inline-flex items-center cursor-pointer auth-mode-selector" data-testid="auth-mode-selector">
|
||||
<MenuDropdown
|
||||
items={menuItems}
|
||||
placement="bottom-end"
|
||||
selectedItemId={authMode}
|
||||
>
|
||||
<div className="flex items-center justify-center auth-mode-label select-none">
|
||||
<div className="flex items-center justify-center auth-mode-label select-none" data-testid="auth-mode-label">
|
||||
{humanizeRequestAuthMode(authMode)} <IconCaretDown className="caret ml-1" size={14} strokeWidth={2} />
|
||||
</div>
|
||||
</MenuDropdown>
|
||||
|
||||
@@ -5,10 +5,11 @@ import { updateCollectionPresets } from 'providers/ReduxStore/slices/collections
|
||||
import { saveCollectionSettings } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import { get } from 'lodash';
|
||||
import Button from 'ui/Button';
|
||||
import { DEFAULT_PRESET_REQUEST_TYPE, PRESET_REQUEST_TYPES } from 'utils/common/constants';
|
||||
|
||||
const PresetsSettings = ({ collection }) => {
|
||||
const dispatch = useDispatch();
|
||||
const initialPresets = { requestType: 'http', requestUrl: '' };
|
||||
const initialPresets = { requestType: DEFAULT_PRESET_REQUEST_TYPE, requestUrl: '' };
|
||||
|
||||
// Get presets from draft.brunoConfig if it exists, otherwise from brunoConfig
|
||||
const currentPresets = collection.draft?.brunoConfig
|
||||
@@ -47,12 +48,13 @@ const PresetsSettings = ({ collection }) => {
|
||||
<div className="flex items-center">
|
||||
<input
|
||||
id="http"
|
||||
data-testid="presets-request-type-http"
|
||||
className="cursor-pointer"
|
||||
type="radio"
|
||||
name="requestType"
|
||||
onChange={handleRequestTypeChange}
|
||||
value="http"
|
||||
checked={(currentPresets.requestType || 'http') === 'http'}
|
||||
value={PRESET_REQUEST_TYPES.HTTP}
|
||||
checked={(currentPresets.requestType || DEFAULT_PRESET_REQUEST_TYPE) === PRESET_REQUEST_TYPES.HTTP}
|
||||
/>
|
||||
<label htmlFor="http" className="ml-1 cursor-pointer select-none">
|
||||
HTTP
|
||||
@@ -60,12 +62,13 @@ const PresetsSettings = ({ collection }) => {
|
||||
|
||||
<input
|
||||
id="graphql"
|
||||
data-testid="presets-request-type-graphql"
|
||||
className="ml-4 cursor-pointer"
|
||||
type="radio"
|
||||
name="requestType"
|
||||
onChange={handleRequestTypeChange}
|
||||
value="graphql"
|
||||
checked={(currentPresets.requestType || 'http') === 'graphql'}
|
||||
value={PRESET_REQUEST_TYPES.GRAPHQL}
|
||||
checked={(currentPresets.requestType || DEFAULT_PRESET_REQUEST_TYPE) === PRESET_REQUEST_TYPES.GRAPHQL}
|
||||
/>
|
||||
<label htmlFor="graphql" className="ml-1 cursor-pointer select-none">
|
||||
GraphQL
|
||||
@@ -73,12 +76,13 @@ const PresetsSettings = ({ collection }) => {
|
||||
|
||||
<input
|
||||
id="grpc"
|
||||
data-testid="presets-request-type-grpc"
|
||||
className="ml-4 cursor-pointer"
|
||||
type="radio"
|
||||
name="requestType"
|
||||
onChange={handleRequestTypeChange}
|
||||
value="grpc"
|
||||
checked={(currentPresets.requestType || 'http') === 'grpc'}
|
||||
value={PRESET_REQUEST_TYPES.GRPC}
|
||||
checked={(currentPresets.requestType || DEFAULT_PRESET_REQUEST_TYPE) === PRESET_REQUEST_TYPES.GRPC}
|
||||
/>
|
||||
<label htmlFor="grpc" className="ml-1 cursor-pointer select-none">
|
||||
gRPC
|
||||
@@ -86,12 +90,13 @@ const PresetsSettings = ({ collection }) => {
|
||||
|
||||
<input
|
||||
id="ws"
|
||||
data-testid="presets-request-type-ws"
|
||||
className="ml-4 cursor-pointer"
|
||||
type="radio"
|
||||
name="requestType"
|
||||
onChange={handleRequestTypeChange}
|
||||
value="ws"
|
||||
checked={(currentPresets.requestType || 'http') === 'ws'}
|
||||
value={PRESET_REQUEST_TYPES.WS}
|
||||
checked={(currentPresets.requestType || DEFAULT_PRESET_REQUEST_TYPE) === PRESET_REQUEST_TYPES.WS}
|
||||
/>
|
||||
<label htmlFor="ws" className="ml-1 cursor-pointer select-none">
|
||||
WebSocket
|
||||
@@ -106,6 +111,7 @@ const PresetsSettings = ({ collection }) => {
|
||||
<div className="flex items-center flex-grow input-container h-full">
|
||||
<input
|
||||
id="request-url"
|
||||
data-testid="presets-request-url"
|
||||
type="text"
|
||||
name="requestUrl"
|
||||
placeholder="Request URL"
|
||||
@@ -123,7 +129,7 @@ const PresetsSettings = ({ collection }) => {
|
||||
</div>
|
||||
|
||||
<div className="mt-6">
|
||||
<Button type="button" size="sm" onClick={handleSave}>
|
||||
<Button type="button" size="sm" data-testid="presets-save-btn" onClick={handleSave}>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -15,6 +15,7 @@ import StyledWrapper from './StyledWrapper';
|
||||
import Vars from './Vars/index';
|
||||
import StatusDot from 'components/StatusDot';
|
||||
import Overview from './Overview/index';
|
||||
import { DEFAULT_PRESET_REQUEST_TYPE } from 'utils/common/constants';
|
||||
|
||||
const CollectionSettings = ({ collection }) => {
|
||||
const dispatch = useDispatch();
|
||||
@@ -60,7 +61,7 @@ const CollectionSettings = ({ collection }) => {
|
||||
? get(collection, 'draft.brunoConfig.protobuf', {})
|
||||
: get(collection, 'brunoConfig.protobuf', {});
|
||||
const presets = collection.draft?.brunoConfig ? get(collection, 'draft.brunoConfig.presets', {}) : get(collection, 'brunoConfig.presets', {});
|
||||
const hasPresets = presets && presets.requestUrl !== '';
|
||||
const hasPresets = presets && ((presets.requestType && presets.requestType !== DEFAULT_PRESET_REQUEST_TYPE) || (presets.requestUrl && presets.requestUrl !== ''));
|
||||
|
||||
const getTabPanel = (tab) => {
|
||||
switch (tab) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import get from 'lodash/get';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import { saveFolderRoot } from 'providers/ReduxStore/slices/collections/actions';
|
||||
@@ -18,8 +18,9 @@ import OAuth1 from 'components/RequestPane/Auth/OAuth1';
|
||||
import WsseAuth from 'components/RequestPane/Auth/WsseAuth';
|
||||
import ApiKeyAuth from 'components/RequestPane/Auth/ApiKeyAuth';
|
||||
import AwsV4Auth from 'components/RequestPane/Auth/AwsV4Auth';
|
||||
import { humanizeRequestAuthMode, getTreePathFromCollectionToItem } from 'utils/collections/index';
|
||||
import { humanizeRequestAuthMode } from 'utils/collections/index';
|
||||
import Button from 'ui/Button';
|
||||
import { getEffectiveAuthSource } from 'utils/auth';
|
||||
|
||||
const GrantTypeComponentMap = ({ collection, folder, updateFolderAuth }) => {
|
||||
const dispatch = useDispatch();
|
||||
@@ -52,41 +53,6 @@ const Auth = ({ collection, folder }) => {
|
||||
let request = get(folderRoot, 'request', {});
|
||||
const authMode = get(folderRoot, 'request.auth.mode');
|
||||
|
||||
const getEffectiveAuthSource = () => {
|
||||
if (authMode !== 'inherit') return null;
|
||||
|
||||
const collectionRoot = collection?.draft?.root || collection?.root || {};
|
||||
const collectionAuth = get(collectionRoot, 'request.auth');
|
||||
let effectiveSource = {
|
||||
type: 'collection',
|
||||
name: 'Collection',
|
||||
auth: collectionAuth
|
||||
};
|
||||
|
||||
// Get path from collection to current folder
|
||||
const folderTreePath = getTreePathFromCollectionToItem(collection, folder);
|
||||
|
||||
// Check parent folders to find closest auth configuration
|
||||
// Skip the last item which is the current folder
|
||||
for (let i = 0; i < folderTreePath.length - 1; i++) {
|
||||
const parentFolder = folderTreePath[i];
|
||||
if (parentFolder.type === 'folder') {
|
||||
const parentFolderRoot = parentFolder?.draft || parentFolder?.root;
|
||||
const folderAuth = get(parentFolderRoot, 'request.auth');
|
||||
if (folderAuth && folderAuth.mode && folderAuth.mode !== 'inherit') {
|
||||
effectiveSource = {
|
||||
type: 'folder',
|
||||
name: parentFolder.name,
|
||||
auth: folderAuth
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return effectiveSource;
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
dispatch(saveFolderRoot(collection.uid, folder.uid));
|
||||
};
|
||||
@@ -98,6 +64,11 @@ const Auth = ({ collection, folder }) => {
|
||||
});
|
||||
};
|
||||
|
||||
const inheritedSource = useMemo(
|
||||
() => (authMode === 'inherit' ? getEffectiveAuthSource(collection, folder) : null),
|
||||
[authMode, folder, collection]
|
||||
);
|
||||
|
||||
const getAuthView = () => {
|
||||
switch (authMode) {
|
||||
case 'basic': {
|
||||
@@ -202,12 +173,11 @@ const Auth = ({ collection, folder }) => {
|
||||
);
|
||||
}
|
||||
case 'inherit': {
|
||||
const source = getEffectiveAuthSource();
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-row w-full mt-2 gap-2">
|
||||
<div>Auth inherited from {source.name}: </div>
|
||||
<div className="inherit-mode-text">{humanizeRequestAuthMode(source.auth?.mode)}</div>
|
||||
<div>Auth inherited from {inheritedSource.name}: </div>
|
||||
<div className="inherit-mode-text" data-testid="inherited-auth-mode">{humanizeRequestAuthMode(inheritedSource.auth?.mode)}</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -81,14 +81,15 @@ const AuthMode = ({ collection, folder }) => {
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<div className="inline-flex items-center cursor-pointer auth-mode-selector">
|
||||
<div className="inline-flex items-center cursor-pointer auth-mode-selector" data-testid="auth-mode-selector">
|
||||
<MenuDropdown
|
||||
items={menuItems}
|
||||
placement="bottom-end"
|
||||
selectedItemId={authMode}
|
||||
showTickMark={true}
|
||||
data-testid="auth-mode-dropdown"
|
||||
>
|
||||
<div className="flex items-center justify-center auth-mode-label select-none">
|
||||
<div className="flex items-center justify-center auth-mode-label select-none" data-testid="auth-mode-label">
|
||||
{humanizeRequestAuthMode(authMode)} <IconCaretDown className="caret ml-1 mr-1" size={14} strokeWidth={2} />
|
||||
</div>
|
||||
</MenuDropdown>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { updatedFolderSettingsSelectedTab } from 'providers/ReduxStore/slices/collections';
|
||||
import { useDispatch } from 'react-redux';
|
||||
@@ -10,7 +10,7 @@ import Vars from './Vars';
|
||||
import Documentation from './Documentation';
|
||||
import Auth from './Auth';
|
||||
import StatusDot from 'components/StatusDot';
|
||||
import get from 'lodash/get';
|
||||
import { hasEffectiveAuth } from 'utils/auth';
|
||||
|
||||
const FolderSettings = ({ collection, folder }) => {
|
||||
const dispatch = useDispatch();
|
||||
@@ -31,8 +31,11 @@ const FolderSettings = ({ collection, folder }) => {
|
||||
const responseVars = folderRoot?.request?.vars?.res || [];
|
||||
const activeVarsCount = requestVars.filter((v) => v.enabled).length + responseVars.filter((v) => v.enabled).length;
|
||||
|
||||
const auth = get(folderRoot, 'request.auth.mode');
|
||||
const hasAuth = auth && auth !== 'none';
|
||||
const folderAuthMode = folder?.draft?.request?.auth?.mode ?? folder?.root?.request?.auth?.mode;
|
||||
const hasAuth = useMemo(
|
||||
() => hasEffectiveAuth(collection, folder),
|
||||
[folder, folderAuthMode, collection]
|
||||
);
|
||||
|
||||
const setTab = (tab) => {
|
||||
dispatch(
|
||||
@@ -95,7 +98,7 @@ const FolderSettings = ({ collection, folder }) => {
|
||||
</div>
|
||||
<div className={getTabClassname('auth')} role="tab" data-testid="folder-settings-tab-auth" onClick={() => setTab('auth')}>
|
||||
Auth
|
||||
{hasAuth && <StatusDot />}
|
||||
{hasAuth && <StatusDot dataTestId="auth" />}
|
||||
</div>
|
||||
<div className={getTabClassname('docs')} role="tab" data-testid="folder-settings-tab-docs" onClick={() => setTab('docs')}>
|
||||
Docs
|
||||
|
||||
@@ -81,14 +81,15 @@ const AuthMode = ({ item, collection }) => {
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<div className="inline-flex items-center cursor-pointer auth-mode-selector">
|
||||
<div className="inline-flex items-center cursor-pointer auth-mode-selector" data-testid="auth-mode-selector">
|
||||
<MenuDropdown
|
||||
items={menuItems}
|
||||
placement="bottom-end"
|
||||
selectedItemId={authMode}
|
||||
showTickMark={true}
|
||||
data-testid="auth-mode-dropdown"
|
||||
>
|
||||
<div className="flex items-center justify-center auth-mode-label select-none">
|
||||
<div className="flex items-center justify-center auth-mode-label select-none" data-testid="auth-mode-label">
|
||||
{humanizeRequestAuthMode(authMode)} <IconCaretDown className="caret ml-1" size={14} strokeWidth={2} />
|
||||
</div>
|
||||
</MenuDropdown>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import get from 'lodash/get';
|
||||
import AwsV4Auth from './AwsV4Auth';
|
||||
import BearerAuth from './BearerAuth';
|
||||
@@ -15,22 +15,11 @@ import ApiKeyAuth from './ApiKeyAuth';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import { humanizeRequestAuthMode } from 'utils/collections';
|
||||
import OAuth2 from './OAuth2/index';
|
||||
import { findItemInCollection, findParentItemInCollection } from 'utils/collections/index';
|
||||
|
||||
const getTreePathFromCollectionToItem = (collection, _item) => {
|
||||
let path = [];
|
||||
let item = findItemInCollection(collection, _item?.uid);
|
||||
while (item) {
|
||||
path.unshift(item);
|
||||
item = findParentItemInCollection(collection, item?.uid);
|
||||
}
|
||||
return path;
|
||||
};
|
||||
import { getEffectiveAuthSource } from 'utils/auth';
|
||||
|
||||
const Auth = ({ item, collection }) => {
|
||||
const dispatch = useDispatch();
|
||||
const authMode = item.draft ? get(item, 'draft.request.auth.mode') : get(item, 'request.auth.mode');
|
||||
const requestTreePath = getTreePathFromCollectionToItem(collection, item);
|
||||
|
||||
// Create a request object to pass to the auth components
|
||||
const request = item.draft
|
||||
@@ -42,34 +31,10 @@ const Auth = ({ item, collection }) => {
|
||||
return dispatch(saveRequest(item.uid, collection.uid));
|
||||
};
|
||||
|
||||
const getEffectiveAuthSource = () => {
|
||||
if (authMode !== 'inherit') return null;
|
||||
|
||||
const collectionRoot = collection?.draft?.root || collection?.root || {};
|
||||
const collectionAuth = get(collectionRoot, 'request.auth');
|
||||
let effectiveSource = {
|
||||
type: 'collection',
|
||||
name: 'Collection',
|
||||
auth: collectionAuth
|
||||
};
|
||||
|
||||
// Check folders in reverse to find the closest auth configuration
|
||||
for (let i of [...requestTreePath].reverse()) {
|
||||
if (i.type === 'folder') {
|
||||
const folderAuth = get(i, 'root.request.auth');
|
||||
if (folderAuth && folderAuth.mode && folderAuth.mode !== 'inherit') {
|
||||
effectiveSource = {
|
||||
type: 'folder',
|
||||
name: i.name,
|
||||
auth: folderAuth
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return effectiveSource;
|
||||
};
|
||||
const inheritedSource = useMemo(
|
||||
() => (authMode === 'inherit' ? getEffectiveAuthSource(collection, item) : null),
|
||||
[authMode, item, collection]
|
||||
);
|
||||
|
||||
const getAuthView = () => {
|
||||
switch (authMode) {
|
||||
@@ -104,12 +69,11 @@ const Auth = ({ item, collection }) => {
|
||||
return <ApiKeyAuth collection={collection} item={item} request={request} save={save} updateAuth={updateAuth} />;
|
||||
}
|
||||
case 'inherit': {
|
||||
const source = getEffectiveAuthSource();
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-row w-full gap-2">
|
||||
<div>Auth inherited from {source.name}: </div>
|
||||
<div className="inherit-mode-text">{humanizeRequestAuthMode(source.auth?.mode)}</div>
|
||||
<div>Auth inherited from {inheritedSource.name}: </div>
|
||||
<div className="inherit-mode-text" data-testid="inherited-auth-mode">{humanizeRequestAuthMode(inheritedSource.auth?.mode)}</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -24,10 +24,12 @@ import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collection
|
||||
import Documentation from 'components/Documentation/index';
|
||||
import useGraphqlSchema from '../GraphQLSchemaActions/useGraphqlSchema';
|
||||
import { findEnvironmentInCollection } from 'utils/collections';
|
||||
import { hasEffectiveAuth } from 'utils/auth';
|
||||
import HeightBoundContainer from 'ui/HeightBoundContainer';
|
||||
import Settings from 'components/RequestPane/Settings';
|
||||
import ResponsiveTabs from 'ui/ResponsiveTabs';
|
||||
import AuthMode from '../Auth/AuthMode/index';
|
||||
import StatusDot from 'components/StatusDot';
|
||||
|
||||
const TAB_CONFIG = [
|
||||
{ key: 'query', label: 'Query' },
|
||||
@@ -172,7 +174,20 @@ const GraphQLRequestPane = ({ item, collection, onSchemaLoad, toggleDocs, handle
|
||||
[dispatch, item.uid]
|
||||
);
|
||||
|
||||
const allTabs = useMemo(() => TAB_CONFIG.map(({ key, label }) => ({ key, label })), []);
|
||||
const itemAuthMode = item.draft?.request?.auth?.mode ?? item.request?.auth?.mode ?? item.root?.request?.auth?.mode;
|
||||
const hasAuth = useMemo(
|
||||
() => hasEffectiveAuth(collection, item),
|
||||
[item, itemAuthMode, collection]
|
||||
);
|
||||
|
||||
const allTabs = useMemo(
|
||||
() => TAB_CONFIG.map(({ key, label }) => ({
|
||||
key,
|
||||
label,
|
||||
indicator: key === 'auth' && hasAuth ? <StatusDot dataTestId="auth" /> : null
|
||||
})),
|
||||
[hasAuth]
|
||||
);
|
||||
|
||||
const handlePrettify = useCallback(() => {
|
||||
if (queryEditorRef.current?.beautifyRequestBody) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import get from 'lodash/get';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import GrpcAuthMode from './GrpcAuthMode';
|
||||
@@ -9,32 +9,32 @@ import OAuth2 from '../../Auth/OAuth2/index';
|
||||
import WsseAuth from '../../Auth/WsseAuth';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import { humanizeRequestAuthMode } from 'utils/collections';
|
||||
import { getTreePathFromCollectionToItem } from 'utils/collections/index';
|
||||
import { getEffectiveAuthSource } from 'utils/auth';
|
||||
import { updateRequestAuthMode, updateAuth } from 'providers/ReduxStore/slices/collections';
|
||||
import { saveRequest } from 'providers/ReduxStore/slices/collections/actions';
|
||||
|
||||
// List of auth modes supported by gRPC
|
||||
// Note: Only header-based auth modes work with gRPC
|
||||
// Complex auth modes like AWS Sig v4, Digest, and NTLM require axios interceptors
|
||||
// and cannot be supported in gRPC requests as of now
|
||||
const supportedGrpcAuthModes = ['basic', 'bearer', 'apikey', 'oauth2', 'wsse', 'none', 'inherit'];
|
||||
import { AUTH_MODES_GRPC } from 'utils/common/constants';
|
||||
|
||||
const GrpcAuth = ({ item, collection }) => {
|
||||
const dispatch = useDispatch();
|
||||
const authMode = item.draft ? get(item, 'draft.request.auth.mode') : get(item, 'request.auth.mode');
|
||||
const requestTreePath = getTreePathFromCollectionToItem(collection, item);
|
||||
|
||||
const request = item.draft
|
||||
? get(item, 'draft.request', {})
|
||||
: get(item, 'request', {});
|
||||
|
||||
const inheritedSource = useMemo(
|
||||
() => (authMode === 'inherit' ? getEffectiveAuthSource(collection, item) : null),
|
||||
[authMode, item, collection]
|
||||
);
|
||||
|
||||
const save = () => {
|
||||
return saveRequest(item.uid, collection.uid);
|
||||
};
|
||||
|
||||
// Reset to 'none' if current auth mode is not supported by gRPC
|
||||
useEffect(() => {
|
||||
if (authMode && !supportedGrpcAuthModes.includes(authMode)) {
|
||||
if (authMode && !AUTH_MODES_GRPC.includes(authMode)) {
|
||||
dispatch(
|
||||
updateRequestAuthMode({
|
||||
itemUid: item.uid,
|
||||
@@ -45,35 +45,6 @@ const GrpcAuth = ({ item, collection }) => {
|
||||
}
|
||||
}, [authMode, collection.uid, dispatch, item.uid]);
|
||||
|
||||
const getEffectiveAuthSource = () => {
|
||||
if (authMode !== 'inherit') return null;
|
||||
|
||||
const collectionRoot = collection?.draft?.root || collection?.root || {};
|
||||
const collectionAuth = get(collectionRoot, 'request.auth');
|
||||
let effectiveSource = {
|
||||
type: 'collection',
|
||||
name: 'Collection',
|
||||
auth: collectionAuth
|
||||
};
|
||||
|
||||
// Check folders in reverse to find the closest auth configuration
|
||||
for (let i of [...requestTreePath].reverse()) {
|
||||
if (i.type === 'folder') {
|
||||
const folderAuth = get(i, 'root.request.auth');
|
||||
if (folderAuth && folderAuth.mode && folderAuth.mode !== 'none' && folderAuth.mode !== 'inherit') {
|
||||
effectiveSource = {
|
||||
type: 'folder',
|
||||
name: i.name,
|
||||
auth: folderAuth
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return effectiveSource;
|
||||
};
|
||||
|
||||
const getAuthView = () => {
|
||||
switch (authMode) {
|
||||
case 'none': {
|
||||
@@ -95,15 +66,13 @@ const GrpcAuth = ({ item, collection }) => {
|
||||
return <WsseAuth collection={collection} item={item} updateAuth={updateAuth} request={request} save={save} />;
|
||||
}
|
||||
case 'inherit': {
|
||||
const source = getEffectiveAuthSource();
|
||||
|
||||
// Only show inherited auth if it's one of the supported types
|
||||
if (source && supportedGrpcAuthModes.includes(source.auth?.mode)) {
|
||||
if (inheritedSource && AUTH_MODES_GRPC.includes(inheritedSource.auth?.mode)) {
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-row w-full gap-2">
|
||||
<div>Auth inherited from {source.name}: </div>
|
||||
<div className="inherit-mode-text">{humanizeRequestAuthMode(source.auth?.mode)}</div>
|
||||
<div>Auth inherited from {inheritedSource.name}: </div>
|
||||
<div className="inherit-mode-text">{humanizeRequestAuthMode(inheritedSource.auth?.mode)}</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -12,6 +12,8 @@ import Documentation from 'components/Documentation/index';
|
||||
import { getPropertyFromDraftOrRequest } from 'utils/collections/index';
|
||||
import ResponsiveTabs from 'ui/ResponsiveTabs';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import { hasEffectiveAuth } from 'utils/auth';
|
||||
import { AUTH_MODES_GRPC } from 'utils/common/constants';
|
||||
|
||||
const GrpcRequestPane = ({ item, collection, handleRun }) => {
|
||||
const dispatch = useDispatch();
|
||||
@@ -53,8 +55,11 @@ const GrpcRequestPane = ({ item, collection, handleRun }) => {
|
||||
const body = getPropertyFromDraftOrRequest(item, 'request.body');
|
||||
const headers = getPropertyFromDraftOrRequest(item, 'request.headers');
|
||||
const docs = getPropertyFromDraftOrRequest(item, 'request.docs');
|
||||
const auth = getPropertyFromDraftOrRequest(item, 'request.auth');
|
||||
|
||||
const itemAuthMode = item.draft?.request?.auth?.mode ?? item.request?.auth?.mode ?? item.root?.request?.auth?.mode;
|
||||
const hasAuth = useMemo(
|
||||
() => hasEffectiveAuth(collection, item, AUTH_MODES_GRPC),
|
||||
[item, itemAuthMode, collection]
|
||||
);
|
||||
const activeHeadersLength = headers.filter((header) => header.enabled).length;
|
||||
const grpcMessagesCount = body?.grpc?.length || 0;
|
||||
|
||||
@@ -88,7 +93,7 @@ const GrpcRequestPane = ({ item, collection, handleRun }) => {
|
||||
{
|
||||
key: 'auth',
|
||||
label: 'Auth',
|
||||
indicator: auth?.mode && auth.mode !== 'none' ? <StatusDot type="default" /> : null
|
||||
indicator: hasAuth ? <StatusDot type="default" dataTestId="auth" /> : null
|
||||
},
|
||||
{
|
||||
key: 'docs',
|
||||
@@ -96,7 +101,7 @@ const GrpcRequestPane = ({ item, collection, handleRun }) => {
|
||||
indicator: docs && docs.length > 0 ? <StatusDot type="default" /> : null
|
||||
}
|
||||
];
|
||||
}, [grpcMessagesCount, isClientStreaming, activeHeadersLength, auth?.mode, docs]);
|
||||
}, [grpcMessagesCount, isClientStreaming, activeHeadersLength, hasAuth, docs]);
|
||||
|
||||
// Initialize tab to 'body' if no tab is currently set
|
||||
useEffect(() => {
|
||||
|
||||
@@ -18,6 +18,7 @@ import StatusDot from 'components/StatusDot';
|
||||
import ResponsiveTabs from 'ui/ResponsiveTabs';
|
||||
import HeightBoundContainer from 'ui/HeightBoundContainer';
|
||||
import AuthMode from '../Auth/AuthMode/index';
|
||||
import { hasEffectiveAuth } from 'utils/auth';
|
||||
|
||||
const TAB_CONFIG = [
|
||||
{ key: 'params', label: 'Params' },
|
||||
@@ -54,7 +55,6 @@ const HttpRequestPane = ({ item, collection }) => {
|
||||
|
||||
const focusedTab = find(tabs, (t) => t.uid === activeTabUid);
|
||||
const requestPaneTab = focusedTab?.requestPaneTab;
|
||||
|
||||
const getProperty = useCallback(
|
||||
(key) => (item.draft ? get(item, `draft.${key}`, []) : get(item, key, [])),
|
||||
[item.draft, item]
|
||||
@@ -86,6 +86,12 @@ const HttpRequestPane = ({ item, collection }) => {
|
||||
[dispatch, item.uid]
|
||||
);
|
||||
|
||||
const itemAuthMode = item.draft?.request?.auth?.mode ?? item.request?.auth?.mode ?? item.root?.request?.auth?.mode;
|
||||
const hasAuth = useMemo(
|
||||
() => hasEffectiveAuth(collection, item),
|
||||
[item, itemAuthMode, collection]
|
||||
);
|
||||
|
||||
const indicators = useMemo(() => {
|
||||
const hasScriptError = item.preRequestScriptErrorMessage || item.postResponseScriptErrorMessage;
|
||||
const hasTestError = item.testScriptErrorMessage;
|
||||
@@ -94,7 +100,7 @@ const HttpRequestPane = ({ item, collection }) => {
|
||||
params: activeCounts.params > 0 ? <sup className="font-medium">{activeCounts.params}</sup> : null,
|
||||
body: body.mode !== 'none' ? <StatusDot /> : null,
|
||||
headers: activeCounts.headers > 0 ? <sup className="font-medium">{activeCounts.headers}</sup> : null,
|
||||
auth: auth.mode !== 'none' ? <StatusDot /> : null,
|
||||
auth: hasAuth ? <StatusDot dataTestId="auth" /> : null,
|
||||
vars: activeCounts.vars > 0 ? <sup className="font-medium">{activeCounts.vars}</sup> : null,
|
||||
script: (script.req || script.res) ? (hasScriptError ? <StatusDot type="error" /> : <StatusDot />) : null,
|
||||
assert: activeCounts.assertions > 0 ? <sup className="font-medium">{activeCounts.assertions}</sup> : null,
|
||||
@@ -102,7 +108,7 @@ const HttpRequestPane = ({ item, collection }) => {
|
||||
docs: docs?.length > 0 ? <StatusDot /> : null,
|
||||
settings: tags?.length > 0 ? <StatusDot /> : null
|
||||
};
|
||||
}, [activeCounts, body.mode, auth.mode, script, item.preRequestScriptErrorMessage, item.postResponseScriptErrorMessage, item.testScriptErrorMessage, tests, docs, tags]);
|
||||
}, [activeCounts, body.mode, hasAuth, script, item.preRequestScriptErrorMessage, item.postResponseScriptErrorMessage, item.testScriptErrorMessage, tests, docs, tags]);
|
||||
|
||||
const allTabs = useMemo(
|
||||
() => TAB_CONFIG.map(({ key, label }) => ({ key, label, indicator: indicators[key] })),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import get from 'lodash/get';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import BearerAuth from '../../Auth/BearerAuth';
|
||||
@@ -6,16 +6,15 @@ import BasicAuth from '../../Auth/BasicAuth';
|
||||
import ApiKeyAuth from '../../Auth/ApiKeyAuth';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import { humanizeRequestAuthMode } from 'utils/collections';
|
||||
import { getTreePathFromCollectionToItem } from 'utils/collections/index';
|
||||
import { getEffectiveAuthSource } from 'utils/auth';
|
||||
import { updateRequestAuthMode, updateAuth } from 'providers/ReduxStore/slices/collections';
|
||||
import { saveRequest } from 'providers/ReduxStore/slices/collections/actions';
|
||||
|
||||
const supportedAuthModes = ['basic', 'bearer', 'apikey', 'oauth2', 'none', 'inherit'];
|
||||
import { AUTH_MODES_WS } from 'utils/common/constants';
|
||||
|
||||
const WSAuth = ({ item, collection }) => {
|
||||
const dispatch = useDispatch();
|
||||
const authMode = item.draft ? get(item, 'draft.request.auth.mode') : get(item, 'request.auth.mode');
|
||||
const requestTreePath = getTreePathFromCollectionToItem(collection, item);
|
||||
|
||||
const request = item.draft
|
||||
? get(item, 'draft.request', {})
|
||||
@@ -25,9 +24,14 @@ const WSAuth = ({ item, collection }) => {
|
||||
return saveRequest(item.uid, collection.uid);
|
||||
};
|
||||
|
||||
const inheritedSource = useMemo(
|
||||
() => (authMode === 'inherit' ? getEffectiveAuthSource(collection, item) : null),
|
||||
[authMode, item, collection]
|
||||
);
|
||||
|
||||
// Reset to 'none' if current auth mode is not supported
|
||||
useEffect(() => {
|
||||
if (authMode && !supportedAuthModes.includes(authMode)) {
|
||||
if (authMode && !AUTH_MODES_WS.includes(authMode)) {
|
||||
dispatch(updateRequestAuthMode({
|
||||
itemUid: item.uid,
|
||||
collectionUid: collection.uid,
|
||||
@@ -36,35 +40,6 @@ const WSAuth = ({ item, collection }) => {
|
||||
}
|
||||
}, [authMode, collection.uid, dispatch, item.uid]);
|
||||
|
||||
const getEffectiveAuthSource = () => {
|
||||
if (authMode !== 'inherit') return null;
|
||||
|
||||
const collectionRoot = collection?.draft?.root || collection?.root || {};
|
||||
const collectionAuth = get(collectionRoot, 'request.auth');
|
||||
let effectiveSource = {
|
||||
type: 'collection',
|
||||
name: 'Collection',
|
||||
auth: collectionAuth
|
||||
};
|
||||
|
||||
// Check folders in reverse to find the closest auth configuration
|
||||
for (let i of [...requestTreePath].reverse()) {
|
||||
if (i.type === 'folder') {
|
||||
const folderAuth = get(i, 'root.request.auth');
|
||||
if (folderAuth && folderAuth.mode && folderAuth.mode !== 'none' && folderAuth.mode !== 'inherit') {
|
||||
effectiveSource = {
|
||||
type: 'folder',
|
||||
name: i.name,
|
||||
auth: folderAuth
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return effectiveSource;
|
||||
};
|
||||
|
||||
const getAuthView = () => {
|
||||
switch (authMode) {
|
||||
case 'none': {
|
||||
@@ -91,26 +66,24 @@ const WSAuth = ({ item, collection }) => {
|
||||
);
|
||||
}
|
||||
case 'inherit': {
|
||||
const source = getEffectiveAuthSource();
|
||||
|
||||
// Check if inherited auth is OAuth1/OAuth2 - not supported for WebSockets
|
||||
if (source?.auth?.mode === 'oauth1' || source?.auth?.mode === 'oauth2') {
|
||||
if (inheritedSource?.auth?.mode === 'oauth1' || inheritedSource?.auth?.mode === 'oauth2') {
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-row w-full mt-2 gap-2">
|
||||
{source.auth.mode === 'oauth1' ? 'OAuth 1.0' : 'OAuth 2'} not <strong>yet</strong> supported by WebSockets. Using no auth instead.
|
||||
{inheritedSource.auth.mode === 'oauth1' ? 'OAuth 1.0' : 'OAuth 2'} not <strong>yet</strong> supported by WebSockets. Using no auth instead.
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// Only show inherited auth if it's one of the supported types
|
||||
if (source && supportedAuthModes.includes(source.auth?.mode)) {
|
||||
if (inheritedSource && AUTH_MODES_WS.includes(inheritedSource.auth?.mode)) {
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-row w-full gap-2">
|
||||
<div> Auth inherited from {source.name}: </div>
|
||||
<div className="inherit-mode-text">{humanizeRequestAuthMode(source.auth?.mode)}</div>
|
||||
<div> Auth inherited from {inheritedSource.name}: </div>
|
||||
<div className="inherit-mode-text">{humanizeRequestAuthMode(inheritedSource.auth?.mode)}</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -20,6 +20,8 @@ import StyledWrapper from './StyledWrapper';
|
||||
import WSAuth from './WSAuth';
|
||||
import WSAuthMode from './WSAuth/WSAuthMode';
|
||||
import WSSettingsPane from '../WSSettingsPane/index';
|
||||
import { hasEffectiveAuth } from 'utils/auth';
|
||||
import { AUTH_MODES_WS } from 'utils/common/constants';
|
||||
|
||||
const WSRequestPane = ({ item, collection, handleRun }) => {
|
||||
const dispatch = useDispatch();
|
||||
@@ -102,8 +104,11 @@ const WSRequestPane = ({ item, collection, handleRun }) => {
|
||||
|
||||
const headers = getPropertyFromDraftOrRequest(item, 'request.headers');
|
||||
const docs = getPropertyFromDraftOrRequest(item, 'request.docs');
|
||||
const auth = getPropertyFromDraftOrRequest(item, 'request.auth');
|
||||
|
||||
const itemAuthMode = item.draft?.request?.auth?.mode ?? item.request?.auth?.mode ?? item.root?.request?.auth?.mode;
|
||||
const hasAuth = useMemo(
|
||||
() => hasEffectiveAuth(collection, item, AUTH_MODES_WS),
|
||||
[item, itemAuthMode, collection]
|
||||
);
|
||||
const activeHeadersLength = headers.filter((header) => header.enabled).length;
|
||||
|
||||
const allTabs = useMemo(() => {
|
||||
@@ -121,7 +126,7 @@ const WSRequestPane = ({ item, collection, handleRun }) => {
|
||||
{
|
||||
key: 'auth',
|
||||
label: 'Auth',
|
||||
indicator: auth.mode !== 'none' ? <StatusDot type="default" /> : null
|
||||
indicator: hasAuth ? <StatusDot type="default" dataTestId="auth" /> : null
|
||||
},
|
||||
{
|
||||
key: 'settings',
|
||||
@@ -134,7 +139,7 @@ const WSRequestPane = ({ item, collection, handleRun }) => {
|
||||
indicator: docs && docs.length > 0 ? <StatusDot type="default" /> : null
|
||||
}
|
||||
];
|
||||
}, [activeHeadersLength, auth.mode, docs]);
|
||||
}, [activeHeadersLength, hasAuth, docs]);
|
||||
|
||||
const tabPanel = useMemo(() => {
|
||||
switch (requestPaneTab) {
|
||||
|
||||
@@ -53,7 +53,12 @@ const Timeline = ({ collection, item }) => {
|
||||
useTrackScroll({ ref: wrapperRef, selector: null, onChange: setScroll, initialValue: scroll });
|
||||
const [activeFilter, setActiveFilter] = useState('all');
|
||||
|
||||
const authSource = getEffectiveAuthSource(collection, item);
|
||||
// Get the effective auth source if auth mode is inherit
|
||||
const itemAuthMode = item.draft?.request?.auth?.mode ?? item.request?.auth?.mode ?? item.root?.request?.auth?.mode;
|
||||
const authSource = useMemo(
|
||||
() => getEffectiveAuthSource(collection, item),
|
||||
[item, itemAuthMode, collection]
|
||||
);
|
||||
const isGrpcRequest = item.type === 'grpc-request' || item.type === 'ws-request';
|
||||
|
||||
const entries = useMemo(
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import React from 'react';
|
||||
import DotIcon from 'components/Icons/Dot';
|
||||
|
||||
const StatusDot = ({ type = 'default' }) => (
|
||||
const StatusDot = ({ type = 'default', dataTestId = null }) => (
|
||||
<sup
|
||||
className={`ml-[.125rem] opacity-80 font-medium ${
|
||||
type === 'error' ? 'text-red-500' : ''
|
||||
}`}
|
||||
data-testid={dataTestId ? `status-dot-${dataTestId}` : 'status-dot'}
|
||||
>
|
||||
<DotIcon width="10" />
|
||||
</sup>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { get } from 'lodash';
|
||||
import {
|
||||
getTreePathFromCollectionToItem
|
||||
} from 'utils/collections/index';
|
||||
import { AUTH_MODES } from 'utils/common/constants';
|
||||
|
||||
// Resolve inherited auth by traversing up the folder hierarchy
|
||||
export const resolveInheritedAuth = (item, collection) => {
|
||||
@@ -25,8 +26,9 @@ export const resolveInheritedAuth = (item, collection) => {
|
||||
const collectionAuth = get(collectionRoot, 'request.auth', { mode: 'none' });
|
||||
let effectiveAuth = collectionAuth;
|
||||
|
||||
// Check folders in reverse to find the closest auth configuration
|
||||
for (let i of [...requestTreePath].reverse()) {
|
||||
// Walk ancestor folders from deepest up; pick the first one with a concrete auth mode (skip 'none'/'inherit').
|
||||
for (let idx = requestTreePath.length - 1; idx >= 0; idx--) {
|
||||
const i = requestTreePath[idx];
|
||||
if (i.type === 'folder') {
|
||||
const folderAuth = i?.draft ? get(i, 'draft.request.auth') : get(i, 'root.request.auth');
|
||||
if (folderAuth && folderAuth.mode && folderAuth.mode !== 'none' && folderAuth.mode !== 'inherit') {
|
||||
@@ -41,3 +43,51 @@ export const resolveInheritedAuth = (item, collection) => {
|
||||
auth: effectiveAuth
|
||||
};
|
||||
};
|
||||
|
||||
export const getEffectiveAuthSource = (collection, item) => {
|
||||
const authMode = item?.draft
|
||||
? get(item, 'draft.request.auth.mode')
|
||||
: (get(item, 'request.auth.mode') ?? get(item, 'root.request.auth.mode'));
|
||||
if (authMode !== AUTH_MODES.INHERIT) return null;
|
||||
|
||||
const collectionRoot = collection?.draft?.root || collection?.root || {};
|
||||
const collectionAuth = get(collectionRoot, 'request.auth');
|
||||
let effectiveSource = {
|
||||
type: 'collection',
|
||||
name: 'Collection',
|
||||
auth: collectionAuth
|
||||
};
|
||||
|
||||
const requestTreePath = getTreePathFromCollectionToItem(collection, item);
|
||||
for (let idx = requestTreePath.length - 1; idx >= 0; idx--) {
|
||||
const i = requestTreePath[idx];
|
||||
if (i?.uid === item?.uid) continue;
|
||||
if (i?.type !== 'folder') continue;
|
||||
const folderAuth = i?.draft ? get(i, 'draft.request.auth') : get(i, 'root.request.auth');
|
||||
if (!folderAuth || !folderAuth.mode) continue;
|
||||
if (folderAuth.mode === AUTH_MODES.INHERIT) continue;
|
||||
effectiveSource = {
|
||||
type: 'folder',
|
||||
name: i.name,
|
||||
auth: folderAuth
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
return effectiveSource;
|
||||
};
|
||||
|
||||
// Returns true when an item actually has auth applied — resolves `inherit` up
|
||||
// the chain, then checks that the effective mode is set, not 'none', and (if a
|
||||
// supportedModes list is passed) is one the protocol can apply.
|
||||
export const hasEffectiveAuth = (collection, item, supportedModes) => {
|
||||
const auth = item?.draft
|
||||
? get(item, 'draft.request.auth')
|
||||
: (get(item, 'request.auth') ?? get(item, 'root.request.auth'));
|
||||
const mode = auth?.mode === AUTH_MODES.INHERIT
|
||||
? getEffectiveAuthSource(collection, item)?.auth?.mode
|
||||
: auth?.mode;
|
||||
if (!mode || mode === AUTH_MODES.NONE) return false;
|
||||
if (supportedModes && !supportedModes.includes(mode)) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
import { resolveInheritedAuth } from './index';
|
||||
import { getEffectiveAuthSource, resolveInheritedAuth } from './index';
|
||||
|
||||
jest.mock('utils/collections/index', () => ({
|
||||
// General path finder: walks the collection.items tree until it finds the
|
||||
// item with the matching uid and returns the full path to it.
|
||||
getTreePathFromCollectionToItem: (collection, item) => {
|
||||
const itemUid = item.uid;
|
||||
|
||||
if (itemUid === 'r1') {
|
||||
return [collection.items[0], collection.items[0].items[0]];
|
||||
}
|
||||
return [];
|
||||
const findPath = (items, targetUid, path = []) => {
|
||||
for (const i of items || []) {
|
||||
const next = [...path, i];
|
||||
if (i.uid === targetUid) return next;
|
||||
if (i.items) {
|
||||
const found = findPath(i.items, targetUid, next);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
return findPath(collection.items, item?.uid) || [];
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -17,7 +25,7 @@ const buildCollection = () => {
|
||||
uid: 'c1',
|
||||
root: {
|
||||
request: {
|
||||
auth: { mode: 'bearer', bearer: { token: 'COLLECTION' } }
|
||||
auth: { mode: 'bearer', bearer: { token: 'COLLECTION_LEVEL_TOKEN' } }
|
||||
}
|
||||
},
|
||||
items: [
|
||||
@@ -64,7 +72,7 @@ describe('auth-utils.resolveInheritedAuth', () => {
|
||||
|
||||
const resolved = resolveInheritedAuth(item, collection);
|
||||
expect(resolved.auth.mode).toBe('bearer');
|
||||
expect(resolved.auth.bearer.token).toBe('COLLECTION');
|
||||
expect(resolved.auth.bearer.token).toBe('COLLECTION_LEVEL_TOKEN');
|
||||
});
|
||||
|
||||
it('should return original request when mode is not inherit', () => {
|
||||
@@ -77,3 +85,211 @@ describe('auth-utils.resolveInheritedAuth', () => {
|
||||
expect(resolved.auth.basic.username).toBe('override');
|
||||
});
|
||||
});
|
||||
|
||||
describe('auth-utils.getEffectiveAuthSource', () => {
|
||||
it('returns null when the request mode is not inherit', () => {
|
||||
const collection = buildCollection();
|
||||
const item = collection.items[0].items[0]; // r1
|
||||
item.request.auth = { mode: 'bearer', bearer: { token: 'MOCK_REQUEST_OWN_TOKEN_STRING' } };
|
||||
|
||||
expect(getEffectiveAuthSource(collection, item)).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null when the request has no auth configured', () => {
|
||||
const collection = buildCollection();
|
||||
const item = collection.items[0].items[0];
|
||||
item.request.auth = undefined;
|
||||
|
||||
expect(getEffectiveAuthSource(collection, item)).toBeNull();
|
||||
});
|
||||
|
||||
it('returns the nearest configured folder when request inherits and folder has auth', () => {
|
||||
const collection = buildCollection();
|
||||
const item = collection.items[0].items[0]; // r1, mode 'inherit'
|
||||
|
||||
const source = getEffectiveAuthSource(collection, item);
|
||||
expect(source).toEqual({
|
||||
type: 'folder',
|
||||
name: 'Folder',
|
||||
auth: { mode: 'basic', basic: { username: 'user', password: 'pass' } }
|
||||
});
|
||||
});
|
||||
|
||||
it('falls back to the collection when no ancestor folder has configured auth', () => {
|
||||
const collection = buildCollection();
|
||||
// make the folder also inherit so the walk falls through to the collection
|
||||
collection.items[0].root.request.auth = { mode: 'inherit' };
|
||||
const item = collection.items[0].items[0];
|
||||
|
||||
const source = getEffectiveAuthSource(collection, item);
|
||||
expect(source).toEqual({
|
||||
type: 'collection',
|
||||
name: 'Collection',
|
||||
auth: { mode: 'bearer', bearer: { token: 'COLLECTION_LEVEL_TOKEN' } }
|
||||
});
|
||||
});
|
||||
|
||||
it('skips the item itself when the item is a folder in inherit mode', () => {
|
||||
// Build a parent → child folder chain; child is the item under test.
|
||||
const collection = {
|
||||
uid: 'c1',
|
||||
root: { request: { auth: { mode: 'bearer', bearer: { token: 'COLLECTION_LEVEL_TOKEN' } } } },
|
||||
items: [
|
||||
{
|
||||
uid: 'parent',
|
||||
type: 'folder',
|
||||
name: 'Parent',
|
||||
root: { request: { auth: { mode: 'basic', basic: { username: 'p', password: 'p' } } } },
|
||||
items: [
|
||||
{
|
||||
uid: 'child',
|
||||
type: 'folder',
|
||||
name: 'Child',
|
||||
root: { request: { auth: { mode: 'inherit' } } },
|
||||
items: []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
const child = collection.items[0].items[0];
|
||||
|
||||
const source = getEffectiveAuthSource(collection, child);
|
||||
expect(source).toEqual({
|
||||
type: 'folder',
|
||||
name: 'Parent',
|
||||
auth: { mode: 'basic', basic: { username: 'p', password: 'p' } }
|
||||
});
|
||||
});
|
||||
|
||||
it('prefers the draft mode when item.draft exists', () => {
|
||||
const collection = buildCollection();
|
||||
const item = collection.items[0].items[0];
|
||||
item.request.auth = { mode: 'bearer' }; // saved is not inherit
|
||||
item.draft = { request: { auth: { mode: 'inherit' } } }; // draft is inherit
|
||||
|
||||
const source = getEffectiveAuthSource(collection, item);
|
||||
expect(source).toEqual({
|
||||
type: 'folder',
|
||||
name: 'Folder',
|
||||
auth: { mode: 'basic', basic: { username: 'user', password: 'pass' } }
|
||||
});
|
||||
});
|
||||
|
||||
it('resolves correctly when both draft and saved auth are inherit on a folder whose parent is also a folder', () => {
|
||||
const collection = {
|
||||
uid: 'c1',
|
||||
root: { request: { auth: { mode: 'bearer', bearer: { token: 'COLLECTION_LEVEL_TOKEN' } } } },
|
||||
items: [
|
||||
{
|
||||
uid: 'parent',
|
||||
type: 'folder',
|
||||
name: 'Parent',
|
||||
root: { request: { auth: { mode: 'basic', basic: { username: 'p', password: 'p' } } } },
|
||||
items: [
|
||||
{
|
||||
uid: 'child',
|
||||
type: 'folder',
|
||||
name: 'Child',
|
||||
root: { request: { auth: { mode: 'inherit' } } }, // saved: inherit
|
||||
draft: { request: { auth: { mode: 'inherit' } } }, // draft: inherit
|
||||
items: []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
const child = collection.items[0].items[0];
|
||||
|
||||
const source = getEffectiveAuthSource(collection, child);
|
||||
expect(source).toEqual({
|
||||
type: 'folder',
|
||||
name: 'Parent',
|
||||
auth: { mode: 'basic', basic: { username: 'p', password: 'p' } }
|
||||
});
|
||||
});
|
||||
|
||||
it('handles a folder item without draft using its root.request.auth.mode', () => {
|
||||
// The folder's mode is read from root.request.auth.mode when no draft exists.
|
||||
const collection = {
|
||||
uid: 'c1',
|
||||
root: { request: { auth: { mode: 'bearer', bearer: { token: 'COLLECTION_LEVEL_TOKEN' } } } },
|
||||
items: [
|
||||
{
|
||||
uid: 'folder-inherit',
|
||||
type: 'folder',
|
||||
name: 'FolderInherit',
|
||||
root: { request: { auth: { mode: 'inherit' } } },
|
||||
items: []
|
||||
}
|
||||
]
|
||||
};
|
||||
const folder = collection.items[0];
|
||||
|
||||
const source = getEffectiveAuthSource(collection, folder);
|
||||
expect(source).toEqual({
|
||||
type: 'collection',
|
||||
name: 'Collection',
|
||||
auth: { mode: 'bearer', bearer: { token: 'COLLECTION_LEVEL_TOKEN' } }
|
||||
});
|
||||
});
|
||||
|
||||
it('handles a folder item with draft by reading its draft.request.auth.mode (not the saved root mode)', () => {
|
||||
// Saved mode is 'basic' (would return null since not inherit), but draft is 'inherit'
|
||||
// so the walk should run and resolve to the collection.
|
||||
const collection = {
|
||||
uid: 'c1',
|
||||
root: { request: { auth: { mode: 'bearer', bearer: { token: 'COLLECTION_LEVEL_TOKEN' } } } },
|
||||
items: [
|
||||
{
|
||||
uid: 'folder-draft-inherit',
|
||||
type: 'folder',
|
||||
name: 'FolderDraftInherit',
|
||||
root: { request: { auth: { mode: 'basic', basic: { username: 'saved', password: 'saved' } } } },
|
||||
draft: { request: { auth: { mode: 'inherit' } } },
|
||||
items: []
|
||||
}
|
||||
]
|
||||
};
|
||||
const folder = collection.items[0];
|
||||
|
||||
const source = getEffectiveAuthSource(collection, folder);
|
||||
expect(source).toEqual({
|
||||
type: 'collection',
|
||||
name: 'Collection',
|
||||
auth: { mode: 'bearer', bearer: { token: 'COLLECTION_LEVEL_TOKEN' } }
|
||||
});
|
||||
});
|
||||
|
||||
it('skips ancestor folders whose auth.mode is itself "inherit"', () => {
|
||||
// Parent folder also inherits — walk should continue past it to collection.
|
||||
const collection = {
|
||||
uid: 'c1',
|
||||
root: { request: { auth: { mode: 'bearer', bearer: { token: 'COLLECTION_LEVEL_TOKEN' } } } },
|
||||
items: [
|
||||
{
|
||||
uid: 'parent',
|
||||
type: 'folder',
|
||||
name: 'Parent',
|
||||
root: { request: { auth: { mode: 'inherit' } } },
|
||||
items: [
|
||||
{
|
||||
uid: 'r1',
|
||||
type: 'request',
|
||||
name: 'Request',
|
||||
request: { auth: { mode: 'inherit' } }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
const item = collection.items[0].items[0];
|
||||
|
||||
const source = getEffectiveAuthSource(collection, item);
|
||||
expect(source).toEqual({
|
||||
type: 'collection',
|
||||
name: 'Collection',
|
||||
auth: { mode: 'bearer', bearer: { token: 'COLLECTION_LEVEL_TOKEN' } }
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,47 @@
|
||||
export const REQUEST_TYPES = ['http-request', 'graphql-request', 'grpc-request', 'ws-request'];
|
||||
|
||||
export const DEFAULT_COLLECTION_FORMAT = 'yml';
|
||||
|
||||
export const PRESET_REQUEST_TYPES = {
|
||||
HTTP: 'http',
|
||||
GRAPHQL: 'graphql',
|
||||
GRPC: 'grpc',
|
||||
WS: 'ws'
|
||||
};
|
||||
|
||||
export const DEFAULT_PRESET_REQUEST_TYPE = PRESET_REQUEST_TYPES.HTTP;
|
||||
|
||||
export const AUTH_MODES = {
|
||||
AWSV4: 'awsv4',
|
||||
BASIC: 'basic',
|
||||
BEARER: 'bearer',
|
||||
DIGEST: 'digest',
|
||||
NTLM: 'ntlm',
|
||||
OAUTH1: 'oauth1',
|
||||
OAUTH2: 'oauth2',
|
||||
WSSE: 'wsse',
|
||||
APIKEY: 'apikey',
|
||||
NONE: 'none',
|
||||
INHERIT: 'inherit'
|
||||
};
|
||||
|
||||
// Auth modes supported by WS protocol.
|
||||
export const AUTH_MODES_WS = [
|
||||
AUTH_MODES.BASIC,
|
||||
AUTH_MODES.BEARER,
|
||||
AUTH_MODES.APIKEY,
|
||||
AUTH_MODES.OAUTH2,
|
||||
AUTH_MODES.NONE,
|
||||
AUTH_MODES.INHERIT
|
||||
];
|
||||
|
||||
// Auth modes supported by GRPC protocol
|
||||
export const AUTH_MODES_GRPC = [
|
||||
AUTH_MODES.BASIC,
|
||||
AUTH_MODES.BEARER,
|
||||
AUTH_MODES.APIKEY,
|
||||
AUTH_MODES.OAUTH2,
|
||||
AUTH_MODES.WSSE,
|
||||
AUTH_MODES.NONE,
|
||||
AUTH_MODES.INHERIT
|
||||
];
|
||||
|
||||
@@ -2,7 +2,7 @@ import { toOpenCollectionAuth, toOpenCollectionHeaders, toOpenCollectionScripts,
|
||||
import { toOpenCollectionEnvironments } from "./environment";
|
||||
import { toOpenCollectionFolder } from "./folder";
|
||||
import { toOpenCollectionItems } from "./items";
|
||||
import { BrunoCollection, BrunoCollectionRoot, BrunoConfig, ClientCertificate, CollectionConfig, OpenCollection, PemCertificate, Pkcs12Certificate, Protobuf } from "./types";
|
||||
import { BrunoCollection, BrunoCollectionRoot, BrunoConfig, BrunoPresets, ClientCertificate, CollectionConfig, OpenCollection, PemCertificate, Pkcs12Certificate, Protobuf } from "./types";
|
||||
|
||||
const toOpenCollectionConfig = (brunoConfig: BrunoConfig | undefined): CollectionConfig | undefined => {
|
||||
if (!brunoConfig) {
|
||||
@@ -147,10 +147,7 @@ export const brunoToOpenCollection = (collection: BrunoCollection): OpenCollecti
|
||||
|
||||
const brunoExtension: {
|
||||
ignore?: string[];
|
||||
presets?: {
|
||||
requestType?: string;
|
||||
requestUrl?: string;
|
||||
};
|
||||
presets?: BrunoPresets;
|
||||
} = {};
|
||||
|
||||
if ((collection.brunoConfig as BrunoConfig)?.ignore?.length) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { OpenCollection } from "@opencollection/types";
|
||||
import { BrunoCollection, BrunoCollectionRoot, BrunoConfig, PemCertificate, Pkcs12Certificate } from "./types";
|
||||
import { BrunoCollection, BrunoCollectionRoot, BrunoConfig, BrunoPresets, PemCertificate, Pkcs12Certificate } from "./types";
|
||||
import { fromOpenCollectionAuth, fromOpenCollectionHeaders, fromOpenCollectionScripts, fromOpenCollectionVariables } from "./common";
|
||||
import { uuid } from "../common";
|
||||
import { fromOpenCollectionItems } from "./items";
|
||||
@@ -9,10 +9,7 @@ import { fromOpenCollectionEnvironments } from "./environment";
|
||||
const fromOpenCollectionConfig = (oc: OpenCollection): BrunoConfig => {
|
||||
const brunoExtension = oc.extensions?.bruno as {
|
||||
ignore?: string[];
|
||||
presets?: {
|
||||
requestType?: string;
|
||||
requestUrl?: string;
|
||||
};
|
||||
presets?: BrunoPresets;
|
||||
} | undefined;
|
||||
|
||||
const ignoreList = brunoExtension && Array.isArray(brunoExtension.ignore)
|
||||
|
||||
@@ -168,15 +168,17 @@ export type {
|
||||
WebSocketMessage as BrunoWsMessage
|
||||
} from '@usebruno/schema-types/requests/websocket';
|
||||
|
||||
export interface BrunoPresets {
|
||||
requestType?: string;
|
||||
requestUrl?: string;
|
||||
}
|
||||
|
||||
export interface BrunoConfig {
|
||||
version?: string;
|
||||
name?: string;
|
||||
type?: string;
|
||||
ignore?: string[];
|
||||
presets?: {
|
||||
requestType?: string;
|
||||
requestUrl?: string;
|
||||
};
|
||||
presets?: BrunoPresets;
|
||||
protobuf?: {
|
||||
protoFiles?: { path: string }[];
|
||||
importPaths?: { path: string; enabled?: boolean }[];
|
||||
|
||||
@@ -809,7 +809,7 @@ const mergeAuth = (collection, request, requestTreePath) => {
|
||||
const folderRoot = i?.draft || i?.root;
|
||||
const folderAuth = get(folderRoot, 'request.auth');
|
||||
// Only consider folders that have a valid auth mode
|
||||
if (folderAuth && folderAuth.mode && folderAuth.mode !== 'none' && folderAuth.mode !== 'inherit') {
|
||||
if (folderAuth && folderAuth.mode && folderAuth.mode !== 'inherit') {
|
||||
effectiveAuth = folderAuth;
|
||||
lastFolderWithAuth = i;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { toBrunoVariables } from './common/variables';
|
||||
import { toBrunoPostResponseVariables } from './common/actions';
|
||||
import { toBrunoScripts } from './common/scripts';
|
||||
import { ensureString } from '../../utils';
|
||||
import type { BrunoPresetsExtension } from '../../types';
|
||||
|
||||
interface ParsedCollection {
|
||||
collectionRoot: FolderRoot;
|
||||
@@ -32,11 +33,11 @@ const parseCollection = (ymlString: string): ParsedCollection => {
|
||||
|
||||
// presets
|
||||
if (brunoExtension?.presets) {
|
||||
const presets = brunoExtension.presets as any;
|
||||
const presets = brunoExtension.presets as BrunoPresetsExtension;
|
||||
if (presets.request) {
|
||||
brunoConfig.presets = {
|
||||
requestType: presets.request.type || [],
|
||||
requestUrl: presets.request.url || []
|
||||
requestType: presets.request.type || '',
|
||||
requestUrl: presets.request.url || ''
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,15 +19,9 @@ const parseFolder = (ymlString: string): FolderRoot => {
|
||||
name: ensureString(info?.name, 'Untitled Folder'),
|
||||
seq: info?.seq || 1
|
||||
},
|
||||
request: null,
|
||||
docs: null
|
||||
};
|
||||
|
||||
// request defaults
|
||||
if (ocFolder.request) {
|
||||
folderRoot.request = {
|
||||
request: {
|
||||
headers: [],
|
||||
auth: null,
|
||||
auth: toBrunoAuth(ocFolder.request?.auth),
|
||||
script: {
|
||||
req: null,
|
||||
res: null
|
||||
@@ -37,40 +31,39 @@ const parseFolder = (ymlString: string): FolderRoot => {
|
||||
res: []
|
||||
},
|
||||
tests: null
|
||||
};
|
||||
},
|
||||
docs: null
|
||||
};
|
||||
|
||||
if (ocFolder.request) {
|
||||
const folderRequest = folderRoot.request!;
|
||||
|
||||
// headers
|
||||
const headers = toBrunoHttpHeaders(ocFolder.request.headers);
|
||||
if (headers) {
|
||||
folderRoot.request.headers = headers;
|
||||
}
|
||||
|
||||
// auth
|
||||
const auth = toBrunoAuth(ocFolder.request.auth);
|
||||
if (auth) {
|
||||
folderRoot.request.auth = auth;
|
||||
folderRequest.headers = headers;
|
||||
}
|
||||
|
||||
// variables
|
||||
const variables = toBrunoVariables(ocFolder.request.variables);
|
||||
const postResponseVars = toBrunoPostResponseVariables((ocFolder.request as any).actions);
|
||||
folderRoot.request.vars = {
|
||||
folderRequest.vars = {
|
||||
req: variables.req,
|
||||
res: postResponseVars
|
||||
};
|
||||
|
||||
// scripts
|
||||
const scripts = toBrunoScripts(ocFolder.request.scripts);
|
||||
if (scripts?.script && folderRoot.request.script) {
|
||||
if (scripts?.script && folderRequest.script) {
|
||||
if (scripts.script.req) {
|
||||
folderRoot.request.script.req = scripts.script.req;
|
||||
folderRequest.script.req = scripts.script.req;
|
||||
}
|
||||
if (scripts.script.res) {
|
||||
folderRoot.request.script.res = scripts.script.res;
|
||||
folderRequest.script.res = scripts.script.res;
|
||||
}
|
||||
}
|
||||
if (scripts?.tests) {
|
||||
folderRoot.request.tests = scripts.tests;
|
||||
folderRequest.tests = scripts.tests;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
7
packages/bruno-filestore/src/formats/yml/tests/fixtures/presets/empty-request.yml
vendored
Normal file
7
packages/bruno-filestore/src/formats/yml/tests/fixtures/presets/empty-request.yml
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
opencollection: 1.0.0
|
||||
info:
|
||||
name: My Collection
|
||||
extensions:
|
||||
bruno:
|
||||
presets:
|
||||
request: {}
|
||||
3
packages/bruno-filestore/src/formats/yml/tests/fixtures/presets/no-presets.yml
vendored
Normal file
3
packages/bruno-filestore/src/formats/yml/tests/fixtures/presets/no-presets.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
opencollection: 1.0.0
|
||||
info:
|
||||
name: My Collection
|
||||
6
packages/bruno-filestore/src/formats/yml/tests/fixtures/presets/no-request-key.yml
vendored
Normal file
6
packages/bruno-filestore/src/formats/yml/tests/fixtures/presets/no-request-key.yml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
opencollection: 1.0.0
|
||||
info:
|
||||
name: My Collection
|
||||
extensions:
|
||||
bruno:
|
||||
presets: {}
|
||||
24
packages/bruno-filestore/src/formats/yml/tests/fixtures/presets/type-only-realistic.yml
vendored
Normal file
24
packages/bruno-filestore/src/formats/yml/tests/fixtures/presets/type-only-realistic.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
opencollection: 1.0.0
|
||||
|
||||
info:
|
||||
name: md8
|
||||
config:
|
||||
proxy:
|
||||
inherit: true
|
||||
config:
|
||||
protocol: http
|
||||
hostname: ""
|
||||
port: ""
|
||||
auth:
|
||||
username: ""
|
||||
password: ""
|
||||
bypassProxy: ""
|
||||
bundled: false
|
||||
extensions:
|
||||
bruno:
|
||||
ignore:
|
||||
- node_modules
|
||||
- .git
|
||||
presets:
|
||||
request:
|
||||
type: graphql
|
||||
25
packages/bruno-filestore/src/formats/yml/tests/fixtures/presets/url-only-realistic.yml
vendored
Normal file
25
packages/bruno-filestore/src/formats/yml/tests/fixtures/presets/url-only-realistic.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
opencollection: 1.0.0
|
||||
|
||||
info:
|
||||
name: md8
|
||||
config:
|
||||
proxy:
|
||||
inherit: true
|
||||
config:
|
||||
protocol: http
|
||||
hostname: ""
|
||||
port: ""
|
||||
auth:
|
||||
username: ""
|
||||
password: ""
|
||||
bypassProxy: ""
|
||||
bundled: false
|
||||
extensions:
|
||||
bruno:
|
||||
ignore:
|
||||
- node_modules
|
||||
- .git
|
||||
presets:
|
||||
request:
|
||||
type: http
|
||||
url: https://example.com/graphql
|
||||
9
packages/bruno-filestore/src/formats/yml/tests/fixtures/presets/with-type-and-url.yml
vendored
Normal file
9
packages/bruno-filestore/src/formats/yml/tests/fixtures/presets/with-type-and-url.yml
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
opencollection: 1.0.0
|
||||
info:
|
||||
name: My Collection
|
||||
extensions:
|
||||
bruno:
|
||||
presets:
|
||||
request:
|
||||
type: graphql
|
||||
url: https://example.com/graphql
|
||||
@@ -0,0 +1,63 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import parseCollection from '../parseCollection';
|
||||
|
||||
const loadFixture = (name) =>
|
||||
fs.readFileSync(path.join(__dirname, 'fixtures', 'presets', `${name}.yml`), 'utf8');
|
||||
|
||||
describe('yml parseCollection - presets', () => {
|
||||
it('parses presets when request.type and request.url are present', () => {
|
||||
const { brunoConfig } = parseCollection(loadFixture('with-type-and-url'));
|
||||
|
||||
expect(brunoConfig.presets).toEqual({
|
||||
requestType: 'graphql',
|
||||
requestUrl: 'https://example.com/graphql'
|
||||
});
|
||||
});
|
||||
|
||||
it('defaults requestType and requestUrl to empty strings (not arrays) when request fields are missing', () => {
|
||||
const { brunoConfig } = parseCollection(loadFixture('empty-request'));
|
||||
|
||||
expect(brunoConfig.presets).toEqual({
|
||||
requestType: '',
|
||||
requestUrl: ''
|
||||
});
|
||||
expect(Array.isArray(brunoConfig.presets.requestType)).toBe(false);
|
||||
expect(Array.isArray(brunoConfig.presets.requestUrl)).toBe(false);
|
||||
});
|
||||
|
||||
it('does not set presets when the extension has no presets block', () => {
|
||||
const { brunoConfig } = parseCollection(loadFixture('no-presets'));
|
||||
|
||||
expect(brunoConfig.presets).toBeUndefined();
|
||||
});
|
||||
|
||||
it('does not set presets when presets exists but request key is absent', () => {
|
||||
const { brunoConfig } = parseCollection(loadFixture('no-request-key'));
|
||||
|
||||
expect(brunoConfig.presets).toBeUndefined();
|
||||
});
|
||||
|
||||
it('parses a realistic collection with only request.type set (no url) — defaults url to empty string', () => {
|
||||
const { brunoConfig } = parseCollection(loadFixture('type-only-realistic'));
|
||||
|
||||
expect(brunoConfig.presets).toEqual({
|
||||
requestType: 'graphql',
|
||||
requestUrl: ''
|
||||
});
|
||||
expect(Array.isArray(brunoConfig.presets.requestUrl)).toBe(false);
|
||||
expect(brunoConfig.ignore).toEqual(['node_modules', '.git']);
|
||||
});
|
||||
|
||||
it('parses a realistic collection with request.type http and request.url set', () => {
|
||||
const { brunoConfig } = parseCollection(loadFixture('url-only-realistic'));
|
||||
|
||||
expect(brunoConfig.presets).toEqual({
|
||||
requestType: 'http',
|
||||
requestUrl: 'https://example.com/graphql'
|
||||
});
|
||||
expect(Array.isArray(brunoConfig.presets.requestType)).toBe(false);
|
||||
expect(Array.isArray(brunoConfig.presets.requestUrl)).toBe(false);
|
||||
expect(brunoConfig.ignore).toEqual(['node_modules', '.git']);
|
||||
});
|
||||
});
|
||||
@@ -20,3 +20,10 @@ export interface WorkerTask {
|
||||
export interface Lane {
|
||||
maxSize: number;
|
||||
}
|
||||
|
||||
export interface BrunoPresetsExtension {
|
||||
request?: {
|
||||
type?: string;
|
||||
url?: string;
|
||||
};
|
||||
}
|
||||
|
||||
113
tests/auth/auth-mode/effective-auth-mode.spec.ts
Normal file
113
tests/auth/auth-mode/effective-auth-mode.spec.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import { test, expect } from '../../../playwright';
|
||||
import { buildCommonLocators, closeAllCollections, createCollection, createFolder, selectAuthMode } from '../../utils/page';
|
||||
import { AUTH_MODE_LABELS } from '../../utils/constants';
|
||||
|
||||
test.describe('Effective auth mode resolution', () => {
|
||||
test.afterEach(async ({ page }) => {
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test('Nested folder with Inherit should pick up its immediate parent folder, not a grandparent', async ({ page, createTmpDir }) => {
|
||||
const collectionName = 'effective-auth-mode-collection';
|
||||
const locators = buildCommonLocators(page);
|
||||
|
||||
await test.step('Create a collection', async () => {
|
||||
await createCollection(page, collectionName, await createTmpDir());
|
||||
});
|
||||
|
||||
await test.step('Create folder-1 inside the collection and set auth type for folder-1 as Bearer Token', async () => {
|
||||
await createFolder(page, 'folder-1', collectionName, true);
|
||||
await locators.sidebar.folder('folder-1').dblclick();
|
||||
await locators.paneTabs.folderSettingsTab('auth').click();
|
||||
await selectAuthMode(page, AUTH_MODE_LABELS.BEARER);
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
});
|
||||
|
||||
await test.step('Create folder-2 inside folder-1 and set auth type for folder-2 as Basic Auth', async () => {
|
||||
await createFolder(page, 'folder-2', 'folder-1', false);
|
||||
await locators.sidebar.folder('folder-2').dblclick();
|
||||
await locators.paneTabs.folderSettingsTab('auth').click();
|
||||
await selectAuthMode(page, AUTH_MODE_LABELS.BASIC);
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
});
|
||||
|
||||
await test.step('Create folder-3 inside folder-2 and set auth type for folder-3 as Inherit', async () => {
|
||||
await createFolder(page, 'folder-3', 'folder-2', false);
|
||||
await locators.sidebar.folder('folder-3').dblclick();
|
||||
await locators.paneTabs.folderSettingsTab('auth').click();
|
||||
await selectAuthMode(page, AUTH_MODE_LABELS.INHERIT);
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
});
|
||||
|
||||
await test.step('Verify folder-3 should inherit auth from folder-2', async () => {
|
||||
await expect(page.getByText('Auth inherited from folder-2:')).toBeVisible();
|
||||
await expect(locators.auth.inheritedMode()).toHaveText(AUTH_MODE_LABELS.BASIC);
|
||||
});
|
||||
});
|
||||
|
||||
test('Child folder with Inherit should pick up parent folder set to No Auth (not fall through to collection)', async ({ page, createTmpDir }) => {
|
||||
const collectionName = 'no-auth-inherit-collection';
|
||||
const locators = buildCommonLocators(page);
|
||||
|
||||
await test.step('Create a collection', async () => {
|
||||
await createCollection(page, collectionName, await createTmpDir());
|
||||
});
|
||||
|
||||
await test.step('Set auth type for the collection as Basic Auth', async () => {
|
||||
await locators.paneTabs.collectionSettingsTab('auth').click();
|
||||
await selectAuthMode(page, AUTH_MODE_LABELS.BASIC);
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
});
|
||||
|
||||
await test.step('Create folder-1 inside the collection and set auth type for folder-1 as No Auth', async () => {
|
||||
await createFolder(page, 'folder-1', collectionName, true);
|
||||
await locators.sidebar.folder('folder-1').dblclick();
|
||||
await locators.paneTabs.folderSettingsTab('auth').click();
|
||||
await selectAuthMode(page, AUTH_MODE_LABELS.NONE);
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
});
|
||||
|
||||
await test.step('Create folder-2 inside folder-1 and set auth type for folder-2 as Inherit', async () => {
|
||||
await createFolder(page, 'folder-2', 'folder-1', false);
|
||||
await locators.sidebar.folder('folder-2').dblclick();
|
||||
await locators.paneTabs.folderSettingsTab('auth').click();
|
||||
await selectAuthMode(page, AUTH_MODE_LABELS.INHERIT);
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
});
|
||||
|
||||
await test.step('Verify folder-2 should inherit No Auth from folder-1 (not fall through to the collection)', async () => {
|
||||
await expect(page.getByText('Auth inherited from folder-1:')).toBeVisible();
|
||||
await expect(locators.auth.inheritedMode()).toHaveText(AUTH_MODE_LABELS.NONE);
|
||||
});
|
||||
});
|
||||
|
||||
test('Auth dropdown shows No Auth as the selected option after picking it', async ({ page, createTmpDir }) => {
|
||||
const collectionName = 'no-auth-dropdown-collection';
|
||||
const locators = buildCommonLocators(page);
|
||||
|
||||
await test.step('Create a collection', async () => {
|
||||
await createCollection(page, collectionName, await createTmpDir());
|
||||
});
|
||||
|
||||
await test.step('Create folder-1 inside the collection and set auth type for folder-1 as No Auth', async () => {
|
||||
await createFolder(page, 'folder-1', collectionName, true);
|
||||
await locators.sidebar.folder('folder-1').dblclick();
|
||||
await locators.paneTabs.folderSettingsTab('auth').click();
|
||||
await selectAuthMode(page, AUTH_MODE_LABELS.NONE);
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
});
|
||||
|
||||
await test.step('Verify the auth mode selector shows No Auth as the current mode', async () => {
|
||||
await expect(locators.auth.modeSelector()).toContainText(AUTH_MODE_LABELS.NONE);
|
||||
});
|
||||
|
||||
await test.step('Reopen the dropdown and verify No Auth is highlighted as the selected option', async () => {
|
||||
await page.locator('.auth-mode-label').first().click();
|
||||
// Bruno marks the selected dropdown item with the `dropdown-item-active` class.
|
||||
const noAuthItem = locators.auth.dropdownItem('none');
|
||||
await expect(noAuthItem).toBeVisible();
|
||||
await expect(noAuthItem).toHaveClass(/dropdown-item-active/);
|
||||
await expect(noAuthItem).toContainText(AUTH_MODE_LABELS.NONE);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,95 @@
|
||||
import { test, expect } from '../../../playwright';
|
||||
import {
|
||||
buildCommonLocators,
|
||||
closeAllCollections,
|
||||
createCollection,
|
||||
createFolder,
|
||||
createRequest,
|
||||
openRequest,
|
||||
saveRequest,
|
||||
selectAuthMode,
|
||||
selectRequestPaneTab,
|
||||
selectResponsePaneTab,
|
||||
sendRequest,
|
||||
typeIntoField
|
||||
} from '../../utils/page';
|
||||
import { AUTH_MODE_LABELS } from '../../utils/constants';
|
||||
|
||||
test.afterEach(async ({ page }) => {
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test('Request inherits No Auth from the folder — collection Bearer Token is overridden', async ({ page, createTmpDir }) => {
|
||||
const collectionName = 'folder-no-auth-inheritance';
|
||||
const locators = buildCommonLocators(page);
|
||||
|
||||
await test.step('Create a collection', async () => {
|
||||
await createCollection(page, collectionName, await createTmpDir());
|
||||
});
|
||||
|
||||
await test.step('Set auth type for the collection as Bearer Token', async () => {
|
||||
await locators.paneTabs.collectionSettingsTab('auth').click();
|
||||
await selectAuthMode(page, AUTH_MODE_LABELS.BEARER);
|
||||
await typeIntoField(page, 'Token', 'your_secret_token');
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
});
|
||||
|
||||
await test.step('Create folder-1 inside the collection and set auth type for folder-1 as No Auth', async () => {
|
||||
await createFolder(page, 'folder-1', collectionName, true);
|
||||
await locators.sidebar.folder('folder-1').dblclick();
|
||||
await locators.paneTabs.folderSettingsTab('auth').click();
|
||||
await selectAuthMode(page, AUTH_MODE_LABELS.NONE);
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
});
|
||||
|
||||
await test.step('Create an HTTP request inside folder-1 and set auth type for the request as Inherit', async () => {
|
||||
const requestName = 'http-request-1';
|
||||
await createRequest(page, requestName, 'folder-1', {
|
||||
inFolder: true,
|
||||
requestType: 'http',
|
||||
method: 'GET',
|
||||
url: 'https://testbench-sanity.usebruno.com/api/auth/bearer/protected'
|
||||
});
|
||||
await openRequest(page, collectionName, requestName);
|
||||
await selectRequestPaneTab(page, 'Auth');
|
||||
await selectAuthMode(page, AUTH_MODE_LABELS.INHERIT);
|
||||
await saveRequest(page);
|
||||
});
|
||||
|
||||
await test.step('Send the request and open the Timeline tab', async () => {
|
||||
await sendRequest(page);
|
||||
await selectResponsePaneTab(page, 'Timeline');
|
||||
});
|
||||
|
||||
await test.step('Verify the response status code is 401 Unauthorized', async () => {
|
||||
await expect(locators.response.statusCode()).toContainText('401');
|
||||
});
|
||||
|
||||
await test.step('Open the latest timeline entry and verify no Authorization header was sent', async () => {
|
||||
const timelineItem = locators.timeline.lastItem();
|
||||
await locators.timeline.itemHeader(timelineItem).click();
|
||||
await expect(timelineItem).toContainText('No Headers found');
|
||||
await locators.timeline.clearButton().click();
|
||||
});
|
||||
|
||||
await test.step('Change folder-1 auth type to Bearer Token with the expected token', async () => {
|
||||
await locators.sidebar.folder('folder-1').dblclick();
|
||||
await locators.paneTabs.folderSettingsTab('auth').click();
|
||||
await selectAuthMode(page, AUTH_MODE_LABELS.BEARER);
|
||||
await typeIntoField(page, 'Token', 'your_secret_token');
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
});
|
||||
|
||||
await test.step('Send the request again and verify the response status code is 200', async () => {
|
||||
await openRequest(page, collectionName, 'http-request-1');
|
||||
await sendRequest(page);
|
||||
await selectResponsePaneTab(page, 'Timeline');
|
||||
await expect(locators.response.statusCode()).toContainText('200');
|
||||
});
|
||||
|
||||
await test.step('Open the latest timeline entry and verify the Bearer token was sent', async () => {
|
||||
const timelineItem = locators.timeline.lastItem();
|
||||
await locators.timeline.itemHeader(timelineItem).click();
|
||||
await expect(timelineItem).toContainText('Bearer your_secret_token');
|
||||
});
|
||||
});
|
||||
147
tests/auth/auth-mode/modified-indicator-for-auth.spec.ts
Normal file
147
tests/auth/auth-mode/modified-indicator-for-auth.spec.ts
Normal file
@@ -0,0 +1,147 @@
|
||||
import { test, expect } from '../../../playwright';
|
||||
import {
|
||||
buildCommonLocators,
|
||||
closeAllCollections,
|
||||
createCollection,
|
||||
createFolder,
|
||||
createRequest,
|
||||
openRequest,
|
||||
saveRequest,
|
||||
selectAuthMode,
|
||||
selectRequestPaneTab
|
||||
} from '../../utils/page';
|
||||
import { AUTH_MODE_LABELS } from '../../utils/constants';
|
||||
|
||||
test.describe('Modified indicator for auth tab', () => {
|
||||
test.afterEach(async ({ page }) => {
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test('Folder Auth tab indicator dot reflects effective auth (shows on inherit, hides on No Auth)', async ({ page, createTmpDir }) => {
|
||||
const collectionName = 'modified-indicator-collection';
|
||||
const locators = buildCommonLocators(page);
|
||||
|
||||
await test.step('Create a collection', async () => {
|
||||
await createCollection(page, collectionName, await createTmpDir());
|
||||
});
|
||||
|
||||
await test.step('Set auth type for the collection as Bearer Token', async () => {
|
||||
await locators.paneTabs.collectionSettingsTab('auth').click();
|
||||
await selectAuthMode(page, AUTH_MODE_LABELS.BEARER);
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
});
|
||||
|
||||
await test.step('Verify the collection auth mode shows Bearer Token', async () => {
|
||||
await expect(locators.auth.modeSelector()).toContainText(AUTH_MODE_LABELS.BEARER);
|
||||
});
|
||||
|
||||
await test.step('Create folder-1 inside the collection and set auth type for folder-1 as Inherit', async () => {
|
||||
await createFolder(page, 'folder-1', collectionName, true);
|
||||
await locators.sidebar.folder('folder-1').dblclick();
|
||||
await locators.paneTabs.folderSettingsTab('auth').click();
|
||||
await selectAuthMode(page, AUTH_MODE_LABELS.INHERIT);
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
});
|
||||
|
||||
await test.step('Verify folder-1 inherits Bearer Token from the collection', async () => {
|
||||
await expect(page.getByText('Auth inherited from Collection:')).toBeVisible();
|
||||
await expect(locators.auth.inheritedMode()).toHaveText(AUTH_MODE_LABELS.BEARER);
|
||||
});
|
||||
|
||||
await test.step('Verify the Auth tab shows the status dot for folder-1 (inheriting Bearer Token)', async () => {
|
||||
await expect(
|
||||
locators.paneTabs.folderSettingsTab('auth').getByTestId('status-dot-auth')
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('Change folder-1 auth type to No Auth', async () => {
|
||||
await selectAuthMode(page, AUTH_MODE_LABELS.NONE);
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
});
|
||||
|
||||
await test.step('Verify the Auth tab does NOT show the status dot for folder-1 (No Auth)', async () => {
|
||||
await expect(
|
||||
locators.paneTabs.folderSettingsTab('auth').getByTestId('status-dot-auth')
|
||||
).toBeHidden();
|
||||
});
|
||||
});
|
||||
|
||||
const requestProtocolCases = [
|
||||
{ protocol: 'HTTP', requestType: 'http' as const, requestName: 'http-request-1', url: 'https://example.com/api' },
|
||||
{ protocol: 'gRPC', requestType: 'grpc' as const, requestName: 'grpc-request-1', url: 'grpc://localhost:50051' },
|
||||
{ protocol: 'WebSocket', requestType: 'ws' as const, requestName: 'ws-request-1', url: 'ws://localhost:8080' },
|
||||
{ protocol: 'GraphQL', requestType: 'graphql' as const, requestName: 'graphql-request-1', url: 'https://example.com/graphql' }
|
||||
];
|
||||
|
||||
for (const { protocol, requestType, requestName, url } of requestProtocolCases) {
|
||||
test(`${protocol} request inheriting auth from its folder shows the modified indicator dot`, async ({ page, createTmpDir }) => {
|
||||
const collectionName = `${protocol.toLowerCase()}-inherit-indicator-collection`;
|
||||
const locators = buildCommonLocators(page);
|
||||
|
||||
await test.step('Create a collection', async () => {
|
||||
await createCollection(page, collectionName, await createTmpDir());
|
||||
});
|
||||
|
||||
await test.step('Set auth type for the collection as Bearer Token', async () => {
|
||||
await locators.paneTabs.collectionSettingsTab('auth').click();
|
||||
await selectAuthMode(page, AUTH_MODE_LABELS.BEARER);
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
});
|
||||
|
||||
await test.step('Create folder-1 inside the collection and set auth type for folder-1 as Basic Auth', async () => {
|
||||
await createFolder(page, 'folder-1', collectionName, true);
|
||||
await locators.sidebar.folder('folder-1').dblclick();
|
||||
await locators.paneTabs.folderSettingsTab('auth').click();
|
||||
await selectAuthMode(page, AUTH_MODE_LABELS.BASIC);
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
});
|
||||
|
||||
await test.step(`Create a ${protocol} request inside folder-1 and set auth type for the request as Inherit`, async () => {
|
||||
await createRequest(page, requestName, 'folder-1', { inFolder: true, requestType, url });
|
||||
await openRequest(page, collectionName, requestName);
|
||||
await selectRequestPaneTab(page, 'Auth');
|
||||
await selectAuthMode(page, AUTH_MODE_LABELS.INHERIT);
|
||||
await saveRequest(page);
|
||||
});
|
||||
|
||||
await test.step(`Verify the ${protocol} request Auth tab shows the status dot (inheriting Basic Auth from folder-1)`, async () => {
|
||||
await expect(
|
||||
locators.paneTabs.responsiveTab('auth').getByTestId('status-dot-auth')
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step(`Change the ${protocol} request auth type to No Auth and verify the dot disappears`, async () => {
|
||||
await selectAuthMode(page, AUTH_MODE_LABELS.NONE);
|
||||
await saveRequest(page);
|
||||
await expect(
|
||||
locators.paneTabs.responsiveTab('auth').getByTestId('status-dot-auth')
|
||||
).toBeHidden();
|
||||
});
|
||||
|
||||
await test.step(`Change the ${protocol} request auth type to Basic Auth and verify the dot appears`, async () => {
|
||||
await selectAuthMode(page, AUTH_MODE_LABELS.BASIC);
|
||||
await saveRequest(page);
|
||||
await expect(
|
||||
locators.paneTabs.responsiveTab('auth').getByTestId('status-dot-auth')
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('Change folder-1 auth type to No Auth', async () => {
|
||||
await locators.sidebar.folder('folder-1').dblclick();
|
||||
await locators.paneTabs.folderSettingsTab('auth').click();
|
||||
await selectAuthMode(page, AUTH_MODE_LABELS.NONE);
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
});
|
||||
|
||||
await test.step(`Set the ${protocol} request auth back to Inherit and verify the dot is hidden (folder is No Auth)`, async () => {
|
||||
await openRequest(page, collectionName, requestName);
|
||||
await selectRequestPaneTab(page, 'Auth');
|
||||
await selectAuthMode(page, AUTH_MODE_LABELS.INHERIT);
|
||||
await saveRequest(page);
|
||||
await expect(
|
||||
locators.paneTabs.responsiveTab('auth').getByTestId('status-dot-auth')
|
||||
).toBeHidden();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
70
tests/collection/presets-indicator.spec.ts
Normal file
70
tests/collection/presets-indicator.spec.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import type { Locator } from '@playwright/test';
|
||||
import { test, expect } from '../../playwright';
|
||||
import { buildCommonLocators, closeAllCollections, createCollection } from '../utils/page';
|
||||
|
||||
test.describe('Presets status dot in collection settings', () => {
|
||||
test.afterEach(async ({ page }) => {
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test('Presets dot is hidden on a fresh collection and stays visible once a preset has been saved (even at defaults)', async ({
|
||||
page,
|
||||
createTmpDir
|
||||
}) => {
|
||||
const collectionName = 'test-presets-indicator';
|
||||
const locators = buildCommonLocators(page);
|
||||
let presetsTab: Locator;
|
||||
|
||||
await test.step('Create a fresh collection (opens collection settings tab)', async () => {
|
||||
await createCollection(page, collectionName, await createTmpDir());
|
||||
});
|
||||
|
||||
await test.step('Open the Presets sub-tab', async () => {
|
||||
presetsTab = locators.paneTabs.collectionSettingsTab('presets');
|
||||
// visibility of the Presets sub-tab implies the collection settings tab is open
|
||||
await expect(presetsTab).toBeVisible();
|
||||
await presetsTab.click();
|
||||
});
|
||||
|
||||
await test.step('Verify default state: HTTP selected and request URL is empty', async () => {
|
||||
await expect(locators.presets.requestType('http')).toBeChecked();
|
||||
await expect(locators.presets.requestType('graphql')).not.toBeChecked();
|
||||
await expect(locators.presets.requestType('grpc')).not.toBeChecked();
|
||||
await expect(locators.presets.requestType('ws')).not.toBeChecked();
|
||||
await expect(locators.presets.requestUrl()).toHaveValue('');
|
||||
});
|
||||
|
||||
await test.step('Verify Presets dot is NOT visible when HTTP is selected and URL is empty', async () => {
|
||||
await expect(presetsTab.getByTestId('status-dot')).toBeHidden();
|
||||
});
|
||||
|
||||
await test.step('Select gRPC request type and save', async () => {
|
||||
await locators.presets.requestType('grpc').check();
|
||||
await locators.presets.save().click();
|
||||
});
|
||||
|
||||
await test.step('Verify Presets dot appears when a non-default request type is selected', async () => {
|
||||
await expect(presetsTab.getByTestId('status-dot')).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('Switch back to HTTP and set a request URL, then save', async () => {
|
||||
await locators.presets.requestType('http').check();
|
||||
await locators.presets.requestUrl().fill('https://example.com');
|
||||
await locators.presets.save().click();
|
||||
});
|
||||
|
||||
await test.step('Verify Presets dot remains visible when request URL is set', async () => {
|
||||
await expect(presetsTab.getByTestId('status-dot')).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('Clear the request URL with HTTP selected, then save (returns to default values)', async () => {
|
||||
await locators.presets.requestUrl().fill('');
|
||||
await expect(locators.presets.requestType('http')).toBeChecked();
|
||||
await locators.presets.save().click();
|
||||
});
|
||||
|
||||
await test.step('Verify Presets dot is hidden after returning to defaults', async () => {
|
||||
await expect(presetsTab.getByTestId('status-dot')).not.toBeVisible({ timeout: 5000 });
|
||||
});
|
||||
});
|
||||
});
|
||||
13
tests/utils/constants/auth.ts
Normal file
13
tests/utils/constants/auth.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export const AUTH_MODE_LABELS = {
|
||||
AWSV4: 'AWS Sig v4',
|
||||
BASIC: 'Basic Auth',
|
||||
BEARER: 'Bearer Token',
|
||||
DIGEST: 'Digest Auth',
|
||||
NTLM: 'NTLM Auth',
|
||||
OAUTH1: 'OAuth 1.0',
|
||||
OAUTH2: 'OAuth 2.0',
|
||||
WSSE: 'WSSE Auth',
|
||||
APIKEY: 'API Key',
|
||||
INHERIT: 'Inherit',
|
||||
NONE: 'No Auth'
|
||||
} as const;
|
||||
1
tests/utils/constants/index.ts
Normal file
1
tests/utils/constants/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './auth';
|
||||
@@ -97,7 +97,17 @@ export const buildCommonLocators = (page: Page) => ({
|
||||
},
|
||||
oauth2: {
|
||||
grantTypeDropdown: () => page.getByTestId('grant-type-dropdown')
|
||||
}
|
||||
},
|
||||
modeSelector: () => page.getByTestId('auth-mode-selector'),
|
||||
modeLabel: () => page.getByTestId('auth-mode-label'),
|
||||
inheritedMode: () => page.getByTestId('inherited-auth-mode'),
|
||||
dropdownItem: (id: string) => page.getByTestId(`auth-mode-dropdown-${id}`)
|
||||
},
|
||||
presets: {
|
||||
requestType: (type: 'http' | 'graphql' | 'grpc' | 'ws') =>
|
||||
page.getByTestId(`presets-request-type-${type}`),
|
||||
requestUrl: () => page.getByTestId('presets-request-url'),
|
||||
save: () => page.getByTestId('presets-save-btn')
|
||||
},
|
||||
tags: {
|
||||
input: () => page.getByTestId('tag-input').getByRole('textbox'),
|
||||
@@ -119,6 +129,12 @@ export const buildCommonLocators = (page: Page) => ({
|
||||
codeLine: () => page.locator('.response-pane .editor-container .CodeMirror-line'),
|
||||
jsonTreeLine: () => page.locator('.response-pane .object-content')
|
||||
},
|
||||
timeline: {
|
||||
items: () => page.locator('.timeline-item'),
|
||||
lastItem: () => page.locator('.timeline-item').last(),
|
||||
itemHeader: (item: Locator) => item.locator('.oauth-request-item-header'),
|
||||
clearButton: () => page.getByRole('button', { name: 'Clear Timeline' })
|
||||
},
|
||||
plusMenu: {
|
||||
button: () => page.getByTestId('collections-header-add-menu'),
|
||||
createCollection: () => page.locator('.tippy-box .dropdown-item').filter({ hasText: 'Create collection' }),
|
||||
|
||||
Reference in New Issue
Block a user