Auth plugins (#155)

This commit is contained in:
Gregory Schier
2025-01-17 05:53:03 -08:00
committed by GitHub
parent e21df98a30
commit bd322162c8
56 changed files with 5468 additions and 1474 deletions

494
src-tauri/Cargo.lock generated
View File

@@ -392,34 +392,6 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "axum"
version = "0.6.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf"
dependencies = [
"async-trait",
"axum-core 0.3.4",
"bitflags 1.3.2",
"bytes",
"futures-util",
"http 0.2.12",
"http-body 0.4.6",
"hyper 0.14.30",
"itoa 1.0.11",
"matchit",
"memchr",
"mime",
"percent-encoding",
"pin-project-lite",
"rustversion",
"serde",
"sync_wrapper 0.1.2",
"tower 0.4.13",
"tower-layer",
"tower-service",
]
[[package]]
name = "axum"
version = "0.7.5"
@@ -427,11 +399,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf"
dependencies = [
"async-trait",
"axum-core 0.4.3",
"axum-core",
"bytes",
"futures-util",
"http 1.1.0",
"http-body 1.0.1",
"http",
"http-body",
"http-body-util",
"itoa 1.0.11",
"matchit",
@@ -447,23 +419,6 @@ dependencies = [
"tower-service",
]
[[package]]
name = "axum-core"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c"
dependencies = [
"async-trait",
"bytes",
"futures-util",
"http 0.2.12",
"http-body 0.4.6",
"mime",
"rustversion",
"tower-layer",
"tower-service",
]
[[package]]
name = "axum-core"
version = "0.4.3"
@@ -473,8 +428,8 @@ dependencies = [
"async-trait",
"bytes",
"futures-util",
"http 1.1.0",
"http-body 1.0.1",
"http",
"http-body",
"http-body-util",
"mime",
"pin-project-lite",
@@ -1528,17 +1483,11 @@ dependencies = [
[[package]]
name = "eventsource-client"
version = "0.13.0"
source = "git+https://github.com/yaakapp/rust-eventsource-client#e9e1e52421f11f0409179389b997aa49275a8461"
version = "0.14.0"
source = "git+https://github.com/yaakapp/rust-eventsource-client#60e0e3ac5038149c4778dc4979b09b152214f9a8"
dependencies = [
"futures",
"hyper 0.14.30",
"hyper-rustls 0.24.2",
"hyper-timeout 0.4.1",
"log",
"pin-project",
"rand 0.8.5",
"tokio",
]
[[package]]
@@ -1709,21 +1658,6 @@ dependencies = [
"new_debug_unreachable",
]
[[package]]
name = "futures"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.30"
@@ -1810,7 +1744,6 @@ version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
@@ -2136,25 +2069,6 @@ dependencies = [
"syn 2.0.87",
]
[[package]]
name = "h2"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
dependencies = [
"bytes",
"fnv",
"futures-core",
"futures-sink",
"futures-util",
"http 0.2.12",
"indexmap 2.3.0",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "h2"
version = "0.4.5"
@@ -2166,7 +2080,7 @@ dependencies = [
"fnv",
"futures-core",
"futures-sink",
"http 1.1.0",
"http",
"indexmap 2.3.0",
"slab",
"tokio",
@@ -2284,37 +2198,15 @@ dependencies = [
[[package]]
name = "http"
version = "0.2.12"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea"
dependencies = [
"bytes",
"fnv",
"itoa 1.0.11",
]
[[package]]
name = "http"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
dependencies = [
"bytes",
"fnv",
"itoa 1.0.11",
]
[[package]]
name = "http-body"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
dependencies = [
"bytes",
"http 0.2.12",
"pin-project-lite",
]
[[package]]
name = "http-body"
version = "1.0.1"
@@ -2322,7 +2214,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
dependencies = [
"bytes",
"http 1.1.0",
"http",
]
[[package]]
@@ -2333,8 +2225,8 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
dependencies = [
"bytes",
"futures-util",
"http 1.1.0",
"http-body 1.0.1",
"http",
"http-body",
"pin-project-lite",
]
@@ -2358,40 +2250,16 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hyper"
version = "0.14.30"
version = "1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9"
dependencies = [
"bytes",
"futures-channel",
"futures-core",
"futures-util",
"h2 0.3.26",
"http 0.2.12",
"http-body 0.4.6",
"httparse",
"httpdate",
"itoa 1.0.11",
"pin-project-lite",
"socket2",
"tokio",
"tower-service",
"tracing",
"want",
]
[[package]]
name = "hyper"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0"
dependencies = [
"bytes",
"futures-channel",
"futures-util",
"h2 0.4.5",
"http 1.1.0",
"http-body 1.0.1",
"h2",
"http",
"http-body",
"httparse",
"httpdate",
"itoa 1.0.11",
@@ -2403,57 +2271,30 @@ dependencies = [
[[package]]
name = "hyper-rustls"
version = "0.24.2"
version = "0.27.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2"
dependencies = [
"futures-util",
"http 0.2.12",
"hyper 0.14.30",
"log",
"rustls 0.21.12",
"rustls-native-certs 0.6.3",
"tokio",
"tokio-rustls 0.24.1",
]
[[package]]
name = "hyper-rustls"
version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333"
dependencies = [
"futures-util",
"http 1.1.0",
"hyper 1.4.1",
"http",
"hyper",
"hyper-util",
"rustls 0.23.21",
"rustls",
"rustls-pki-types",
"rustls-platform-verifier",
"tokio",
"tokio-rustls 0.26.0",
"tokio-rustls",
"tower-service",
"webpki-roots",
]
[[package]]
name = "hyper-timeout"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1"
dependencies = [
"hyper 0.14.30",
"pin-project-lite",
"tokio",
"tokio-io-timeout",
]
[[package]]
name = "hyper-timeout"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793"
dependencies = [
"hyper 1.4.1",
"hyper",
"hyper-util",
"pin-project-lite",
"tokio",
@@ -2468,7 +2309,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
dependencies = [
"bytes",
"http-body-util",
"hyper 1.4.1",
"hyper",
"hyper-util",
"native-tls",
"tokio",
@@ -2485,9 +2326,9 @@ dependencies = [
"bytes",
"futures-channel",
"futures-util",
"http 1.1.0",
"http-body 1.0.1",
"hyper 1.4.1",
"http",
"http-body",
"hyper",
"pin-project-lite",
"socket2",
"tokio",
@@ -2672,15 +2513,6 @@ dependencies = [
"nom 4.2.3",
]
[[package]]
name = "itertools"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.13.0"
@@ -4114,22 +3946,12 @@ dependencies = [
[[package]]
name = "prost"
version = "0.12.6"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29"
checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec"
dependencies = [
"bytes",
"prost-derive 0.12.6",
]
[[package]]
name = "prost"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f"
dependencies = [
"bytes",
"prost-derive 0.13.3",
"prost-derive",
]
[[package]]
@@ -4140,14 +3962,14 @@ checksum = "5bb182580f71dd070f88d01ce3de9f4da5021db7115d2e1c3605a754153b77c1"
dependencies = [
"bytes",
"heck 0.5.0",
"itertools 0.13.0",
"itertools",
"log",
"multimap",
"once_cell",
"petgraph",
"prettyplease",
"prost 0.13.3",
"prost-types 0.13.3",
"prost",
"prost-types",
"regex",
"syn 2.0.87",
"tempfile",
@@ -4155,25 +3977,12 @@ dependencies = [
[[package]]
name = "prost-derive"
version = "0.12.6"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1"
checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3"
dependencies = [
"anyhow",
"itertools 0.12.1",
"proc-macro2",
"quote",
"syn 2.0.87",
]
[[package]]
name = "prost-derive"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5"
dependencies = [
"anyhow",
"itertools 0.13.0",
"itertools",
"proc-macro2",
"quote",
"syn 2.0.87",
@@ -4181,24 +3990,24 @@ dependencies = [
[[package]]
name = "prost-reflect"
version = "0.12.0"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "057237efdb71cf4b3f9396302a3d6599a92fa94063ba537b66130980ea9909f3"
checksum = "bc9647f03b808b79abca8408b1609be9887ba90453c940d00332a60eeb6f5748"
dependencies = [
"base64 0.21.7",
"base64 0.22.1",
"once_cell",
"prost 0.12.6",
"prost",
"prost-reflect-derive",
"prost-types 0.12.6",
"prost-types",
"serde",
"serde-value",
]
[[package]]
name = "prost-reflect-derive"
version = "0.12.0"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "172da1212c02be2c94901440cb27183cd92bff00ebacca5c323bf7520b8f9c04"
checksum = "f4fce6b22f15cc8d8d400a2b98ad29202b33bd56c7d9ddd815bc803a807ecb65"
dependencies = [
"proc-macro2",
"quote",
@@ -4207,20 +4016,11 @@ dependencies = [
[[package]]
name = "prost-types"
version = "0.12.6"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0"
checksum = "cc2f1e56baa61e93533aebc21af4d2134b70f66275e0fcdf3cbe43d77ff7e8fc"
dependencies = [
"prost 0.12.6",
]
[[package]]
name = "prost-types"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4759aa0d3a6232fb8dbdb97b61de2c20047c68aca932c7ed76da9d788508d670"
dependencies = [
"prost 0.13.3",
"prost",
]
[[package]]
@@ -4288,7 +4088,7 @@ dependencies = [
"quinn-proto",
"quinn-udp",
"rustc-hash",
"rustls 0.23.21",
"rustls",
"socket2",
"thiserror 1.0.63",
"tokio",
@@ -4305,7 +4105,7 @@ dependencies = [
"rand 0.8.5",
"ring",
"rustc-hash",
"rustls 0.23.21",
"rustls",
"slab",
"thiserror 1.0.63",
"tinyvec",
@@ -4536,12 +4336,12 @@ dependencies = [
"encoding_rs",
"futures-core",
"futures-util",
"h2 0.4.5",
"http 1.1.0",
"http-body 1.0.1",
"h2",
"http",
"http-body",
"http-body-util",
"hyper 1.4.1",
"hyper-rustls 0.27.3",
"hyper",
"hyper-rustls",
"hyper-tls",
"hyper-util",
"ipnet",
@@ -4554,8 +4354,8 @@ dependencies = [
"percent-encoding",
"pin-project-lite",
"quinn",
"rustls 0.23.21",
"rustls-pemfile 2.1.3",
"rustls",
"rustls-pemfile",
"rustls-pki-types",
"serde",
"serde_json",
@@ -4564,7 +4364,7 @@ dependencies = [
"system-configuration",
"tokio",
"tokio-native-tls",
"tokio-rustls 0.26.0",
"tokio-rustls",
"tokio-util",
"tower 0.5.2",
"tower-service",
@@ -4741,18 +4541,6 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "rustls"
version = "0.21.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
dependencies = [
"log",
"ring",
"rustls-webpki 0.101.7",
"sct",
]
[[package]]
name = "rustls"
version = "0.23.21"
@@ -4762,23 +4550,11 @@ dependencies = [
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki 0.102.8",
"rustls-webpki",
"subtle",
"zeroize",
]
[[package]]
name = "rustls-native-certs"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00"
dependencies = [
"openssl-probe",
"rustls-pemfile 1.0.4",
"schannel",
"security-framework 2.11.1",
]
[[package]]
name = "rustls-native-certs"
version = "0.8.0"
@@ -4786,21 +4562,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a"
dependencies = [
"openssl-probe",
"rustls-pemfile 2.1.3",
"rustls-pemfile",
"rustls-pki-types",
"schannel",
"security-framework 2.11.1",
]
[[package]]
name = "rustls-pemfile"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
dependencies = [
"base64 0.21.7",
]
[[package]]
name = "rustls-pemfile"
version = "2.1.3"
@@ -4828,10 +4595,10 @@ dependencies = [
"jni",
"log",
"once_cell",
"rustls 0.23.21",
"rustls-native-certs 0.8.0",
"rustls",
"rustls-native-certs",
"rustls-platform-verifier-android",
"rustls-webpki 0.102.8",
"rustls-webpki",
"security-framework 3.2.0",
"security-framework-sys",
"webpki-root-certs",
@@ -4844,16 +4611,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
[[package]]
name = "rustls-webpki"
version = "0.101.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "rustls-webpki"
version = "0.102.8"
@@ -4943,16 +4700,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "sct"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "sea-query"
version = "0.32.1"
@@ -5445,8 +5192,8 @@ dependencies = [
"once_cell",
"paste",
"percent-encoding",
"rustls 0.23.21",
"rustls-pemfile 2.1.3",
"rustls",
"rustls-pemfile",
"serde",
"serde_json",
"sha2",
@@ -5854,7 +5601,7 @@ dependencies = [
"glob",
"gtk",
"heck 0.5.0",
"http 1.1.0",
"http",
"http-range",
"jni",
"libc",
@@ -6133,7 +5880,7 @@ dependencies = [
"dirs",
"flate2",
"futures-util",
"http 1.1.0",
"http",
"infer",
"minisign-verify",
"percent-encoding",
@@ -6176,7 +5923,7 @@ checksum = "2274ef891ccc0a8d318deffa9d70053f947664d12d58b9c0d1ae5e89237e01f7"
dependencies = [
"dpi",
"gtk",
"http 1.1.0",
"http",
"jni",
"raw-window-handle",
"serde",
@@ -6194,7 +5941,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3707b40711d3b9f6519150869e358ffbde7c57567fb9b5a8b51150606939b2a0"
dependencies = [
"gtk",
"http 1.1.0",
"http",
"jni",
"log",
"objc2",
@@ -6225,7 +5972,7 @@ dependencies = [
"dunce",
"glob",
"html5ever",
"http 1.1.0",
"http",
"infer",
"json-patch",
"kuchikiki",
@@ -6416,16 +6163,6 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "tokio-io-timeout"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf"
dependencies = [
"pin-project-lite",
"tokio",
]
[[package]]
name = "tokio-macros"
version = "2.4.0"
@@ -6447,23 +6184,13 @@ dependencies = [
"tokio",
]
[[package]]
name = "tokio-rustls"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
dependencies = [
"rustls 0.21.12",
"tokio",
]
[[package]]
name = "tokio-rustls"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
dependencies = [
"rustls 0.23.21",
"rustls",
"rustls-pki-types",
"tokio",
]
@@ -6564,52 +6291,25 @@ dependencies = [
[[package]]
name = "tonic"
version = "0.10.2"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e"
checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52"
dependencies = [
"async-stream",
"async-trait",
"axum 0.6.20",
"base64 0.21.7",
"bytes",
"h2 0.3.26",
"http 0.2.12",
"http-body 0.4.6",
"hyper 0.14.30",
"hyper-timeout 0.4.1",
"percent-encoding",
"pin-project",
"prost 0.12.6",
"tokio",
"tokio-stream",
"tower 0.4.13",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tonic"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38659f4a91aba8598d27821589f5db7dddd94601e7a01b1e485a50e5484c7401"
dependencies = [
"async-stream",
"async-trait",
"axum 0.7.5",
"axum",
"base64 0.22.1",
"bytes",
"h2 0.4.5",
"http 1.1.0",
"http-body 1.0.1",
"h2",
"http",
"http-body",
"http-body-util",
"hyper 1.4.1",
"hyper-timeout 0.5.1",
"hyper",
"hyper-timeout",
"hyper-util",
"percent-encoding",
"pin-project",
"prost 0.13.3",
"prost",
"socket2",
"tokio",
"tokio-stream",
@@ -6634,15 +6334,15 @@ dependencies = [
[[package]]
name = "tonic-reflection"
version = "0.10.2"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fa37c513df1339d197f4ba21d28c918b9ef1ac1768265f11ecb6b7f1cba1b76"
checksum = "878d81f52e7fcfd80026b7fdb6a9b578b3c3653ba987f87f0dce4b64043cba27"
dependencies = [
"prost 0.12.6",
"prost-types 0.12.6",
"prost",
"prost-types",
"tokio",
"tokio-stream",
"tonic 0.10.2",
"tonic",
]
[[package]]
@@ -7703,7 +7403,7 @@ dependencies = [
"gdkx11",
"gtk",
"html5ever",
"http 1.1.0",
"http",
"javascriptcore-rs",
"jni",
"kuchikiki",
@@ -7803,13 +7503,12 @@ dependencies = [
name = "yaak-app"
version = "0.0.0"
dependencies = [
"base64 0.22.1",
"chrono",
"cocoa 0.26.0",
"datetime",
"eventsource-client",
"hex_color",
"http 1.1.0",
"http",
"log",
"mime_guess",
"objc",
@@ -7818,7 +7517,7 @@ dependencies = [
"regex",
"reqwest",
"reqwest_cookie_store",
"rustls 0.23.21",
"rustls",
"rustls-platform-verifier",
"serde",
"serde_json",
@@ -7855,20 +7554,21 @@ dependencies = [
"anyhow",
"async-recursion",
"dunce",
"hyper 0.14.30",
"hyper-rustls 0.24.2",
"hyper",
"hyper-rustls",
"hyper-util",
"log",
"md5",
"prost 0.12.6",
"prost",
"prost-reflect",
"prost-types 0.12.6",
"prost-types",
"serde",
"serde_json",
"tauri",
"tauri-plugin-shell",
"tokio",
"tokio-stream",
"tonic 0.10.2",
"tonic",
"tonic-reflection",
"uuid",
]
@@ -7916,7 +7616,7 @@ dependencies = [
"dunce",
"log",
"path-slash",
"prost 0.13.3",
"prost",
"rand 0.8.5",
"regex",
"serde",
@@ -7925,7 +7625,7 @@ dependencies = [
"tauri-plugin-shell",
"thiserror 2.0.7",
"tokio",
"tonic 0.12.1",
"tonic",
"tonic-build",
"ts-rs",
"yaak-models",

View File

@@ -38,12 +38,11 @@ cocoa = "0.26.0"
openssl-sys = { version = "0.9", features = ["vendored"] } # For Ubuntu installation to work
[dependencies]
base64 = "0.22.0"
chrono = { version = "0.4.31", features = ["serde"] }
datetime = "0.5.2"
eventsource-client = { git = "https://github.com/yaakapp/rust-eventsource-client", version = "0.13.0" }
eventsource-client = { git = "https://github.com/yaakapp/rust-eventsource-client", version = "0.14.0" }
hex_color = "3.0.0"
http = "1"
http = { version = "1.2.0", default-features = false }
log = "0.4.21"
rand = "0.8.5"
regex = "1.10.2"

View File

@@ -1,10 +1,8 @@
use crate::render::render_http_request;
use crate::response_err;
use crate::template_callback::PluginTemplateCallback;
use base64::prelude::BASE64_STANDARD;
use base64::Engine;
use http::header::{ACCEPT, USER_AGENT};
use http::{HeaderMap, HeaderName, HeaderValue};
use http::{HeaderMap, HeaderName, HeaderValue, Uri};
use log::{debug, error, warn};
use mime_guess::Mime;
use reqwest::redirect::Policy;
@@ -32,7 +30,8 @@ use yaak_models::queries::{
get_base_environment, get_http_response, get_or_create_settings, get_workspace,
update_response_if_id, upsert_cookie_jar, UpdateSource,
};
use yaak_plugins::events::{RenderPurpose, WindowContext};
use yaak_plugins::events::{CallHttpAuthenticationRequest, HttpHeader, RenderPurpose, WindowContext};
use yaak_plugins::manager::PluginManager;
pub async fn send_http_request<R: Runtime>(
window: &WebviewWindow<R>,
@@ -42,6 +41,7 @@ pub async fn send_http_request<R: Runtime>(
cookie_jar: Option<CookieJar>,
cancelled_rx: &mut Receiver<bool>,
) -> Result<HttpResponse, String> {
let plugin_manager = window.state::<PluginManager>();
let workspace =
get_workspace(window, &request.workspace_id).await.expect("Failed to get Workspace");
let base_environment = get_base_environment(window, &request.workspace_id)
@@ -160,7 +160,7 @@ pub async fn send_http_request<R: Runtime>(
query_params.push((p.name, p.value));
}
let uri = match http::Uri::from_str(url_string.as_str()) {
let uri = match Uri::from_str(url_string.as_str()) {
Ok(u) => u,
Err(e) => {
return Ok(response_err(
@@ -234,29 +234,6 @@ pub async fn send_http_request<R: Runtime>(
headers.insert(header_name, header_value);
}
if let Some(b) = &rendered_request.authentication_type {
let empty_value = &serde_json::to_value("").unwrap();
let a = rendered_request.authentication;
if b == "basic" {
let username = a.get("username").unwrap_or(empty_value).as_str().unwrap_or_default();
let password = a.get("password").unwrap_or(empty_value).as_str().unwrap_or_default();
let auth = format!("{username}:{password}");
let encoded = BASE64_STANDARD.encode(auth);
headers.insert(
"Authorization",
HeaderValue::from_str(&format!("Basic {}", encoded)).unwrap(),
);
} else if b == "bearer" {
let token = a.get("token").unwrap_or(empty_value).as_str().unwrap_or_default();
headers.insert(
"Authorization",
HeaderValue::from_str(&format!("Bearer {token}")).unwrap(),
);
}
}
let request_body = rendered_request.body;
if let Some(body_type) = &rendered_request.body_type {
if body_type == "graphql" {
@@ -383,7 +360,7 @@ pub async fn send_http_request<R: Runtime>(
// Add headers last, because previous steps may modify them
request_builder = request_builder.headers(headers);
let sendable_req = match request_builder.build() {
let mut sendable_req = match request_builder.build() {
Ok(r) => r,
Err(e) => {
warn!("Failed to build request builder {e:?}");
@@ -391,6 +368,56 @@ pub async fn send_http_request<R: Runtime>(
}
};
// Apply authentication
// Map legacy auth name values from before they were plugins
let auth_plugin_name = match request.authentication_type.clone() {
Some(s) if s == "basic" => Some("@yaakapp/auth-basic".to_string()),
Some(s) if s == "bearer" => Some("@yaakapp/auth-bearer".to_string()),
_ => request.authentication_type.to_owned(),
};
if let Some(plugin_name) = auth_plugin_name {
let req = CallHttpAuthenticationRequest {
config: serde_json::to_value(&request.authentication)
.unwrap()
.as_object()
.unwrap()
.to_owned(),
method: sendable_req.method().to_string(),
url: sendable_req.url().to_string(),
headers: sendable_req
.headers()
.iter()
.map(|(name, value)| HttpHeader {
name: name.to_string(),
value: value.to_str().unwrap_or_default().to_string(),
})
.collect(),
};
let plugin_result =
match plugin_manager.call_http_authentication(window, &plugin_name, req).await {
Ok(r) => r,
Err(e) => {
return Ok(response_err(&*response.lock().await, e.to_string(), window).await);
}
};
{
let url = sendable_req.url_mut();
*url = Url::parse(&plugin_result.url).unwrap();
}
{
let headers = sendable_req.headers_mut();
for header in plugin_result.headers {
headers.insert(
HeaderName::from_str(&header.name).unwrap(),
HeaderValue::from_str(&header.value).unwrap(),
);
}
};
}
let (resp_tx, resp_rx) = oneshot::channel::<Result<Response, reqwest::Error>>();
let (done_tx, done_rx) = oneshot::channel::<HttpResponse>();

View File

@@ -9,8 +9,6 @@ use crate::render::{render_grpc_request, render_http_request, render_json_value,
use crate::template_callback::PluginTemplateCallback;
use crate::updates::{UpdateMode, YaakUpdater};
use crate::window_menu::app_menu;
use base64::prelude::BASE64_STANDARD;
use base64::Engine;
use chrono::Utc;
use eventsource_client::{EventParser, SSE};
use log::{debug, error, info, warn};
@@ -65,11 +63,11 @@ use yaak_models::queries::{
upsert_workspace_meta, BatchUpsertResult, UpdateSource,
};
use yaak_plugins::events::{
BootResponse, CallHttpRequestActionRequest, FilterResponse, FindHttpResponsesResponse,
GetHttpRequestActionsResponse, GetHttpRequestByIdResponse, GetTemplateFunctionsResponse, Icon,
InternalEvent, InternalEventPayload, PromptTextResponse, RenderHttpRequestResponse,
RenderPurpose, SendHttpRequestResponse, ShowToastRequest, TemplateRenderResponse,
WindowContext,
BootResponse, CallHttpAuthenticationRequest, CallHttpRequestActionRequest, FilterResponse,
FindHttpResponsesResponse, GetHttpAuthenticationResponse, GetHttpRequestActionsResponse,
GetHttpRequestByIdResponse, GetTemplateFunctionsResponse, HttpHeader, Icon, InternalEvent,
InternalEventPayload, PromptTextResponse, RenderHttpRequestResponse, RenderPurpose,
SendHttpRequestResponse, ShowToastRequest, TemplateRenderResponse, WindowContext,
};
use yaak_plugins::manager::PluginManager;
use yaak_plugins::plugin_handle::PluginHandle;
@@ -154,7 +152,7 @@ async fn cmd_render_template<R: Runtime>(
RenderPurpose::Preview,
),
)
.await;
.await;
Ok(rendered)
}
@@ -198,6 +196,7 @@ async fn cmd_grpc_go<R: Runtime>(
environment_id: Option<&str>,
proto_files: Vec<String>,
window: WebviewWindow<R>,
plugin_manager: State<'_, PluginManager>,
grpc_handle: State<'_, Mutex<GrpcHandle>>,
) -> Result<String, String> {
let environment = match environment_id {
@@ -210,7 +209,7 @@ async fn cmd_grpc_go<R: Runtime>(
.ok_or("Failed to find GRPC request")?;
let base_environment =
get_base_environment(&window, &req.workspace_id).await.map_err(|e| e.to_string())?;
let req = render_grpc_request(
let mut req = render_grpc_request(
&req,
&base_environment,
environment.as_ref(),
@@ -220,7 +219,7 @@ async fn cmd_grpc_go<R: Runtime>(
RenderPurpose::Send,
),
)
.await;
.await;
let mut metadata = BTreeMap::new();
// Add the rest of metadata
@@ -236,21 +235,37 @@ async fn cmd_grpc_go<R: Runtime>(
metadata.insert(h.name, h.value);
}
if let Some(b) = &req.authentication_type {
let req = req.clone();
let empty_value = &serde_json::to_value("").unwrap();
let a = req.authentication;
// Map legacy auth name values from before they were plugins
let auth_plugin_name = match req.authentication_type.clone() {
Some(s) if s == "basic" => Some("@yaakapp/auth-basic".to_string()),
Some(s) if s == "bearer" => Some("@yaakapp/auth-bearer".to_string()),
_ => req.authentication_type.to_owned(),
};
if let Some(plugin_name) = auth_plugin_name {
let plugin_req = CallHttpAuthenticationRequest {
config: serde_json::to_value(&req.authentication)
.unwrap()
.as_object()
.unwrap()
.to_owned(),
method: "POST".to_string(),
url: req.url.clone(),
headers: metadata
.iter()
.map(|(name, value)| HttpHeader {
name: name.to_string(),
value: value.to_string(),
})
.collect(),
};
let plugin_result = plugin_manager
.call_http_authentication(&window, &plugin_name, plugin_req)
.await
.map_err(|e| e.to_string())?;
if b == "basic" {
let username = a.get("username").unwrap_or(empty_value).as_str().unwrap_or("");
let password = a.get("password").unwrap_or(empty_value).as_str().unwrap_or("");
let auth = format!("{username}:{password}");
let encoded = BASE64_STANDARD.encode(auth);
metadata.insert("Authorization".to_string(), format!("Basic {}", encoded));
} else if b == "bearer" {
let token = a.get("token").unwrap_or(empty_value).as_str().unwrap_or("");
metadata.insert("Authorization".to_string(), format!("Bearer {token}"));
req.url = plugin_result.url;
for header in plugin_result.headers {
metadata.insert(header.name, header.value);
}
}
@@ -269,8 +284,8 @@ async fn cmd_grpc_go<R: Runtime>(
},
&UpdateSource::Window,
)
.await
.map_err(|e| e.to_string())?
.await
.map_err(|e| e.to_string())?
};
let conn_id = conn.id.clone();
@@ -322,8 +337,8 @@ async fn cmd_grpc_go<R: Runtime>(
},
&UpdateSource::Window,
)
.await
.map_err(|e| e.to_string())?;
.await
.map_err(|e| e.to_string())?;
return Ok(conn_id);
}
};
@@ -378,7 +393,7 @@ async fn cmd_grpc_go<R: Runtime>(
RenderPurpose::Send,
),
)
.await
.await
})
})
};
@@ -396,8 +411,8 @@ async fn cmd_grpc_go<R: Runtime>(
},
&UpdateSource::Window,
)
.await
.unwrap();
.await
.unwrap();
});
return;
}
@@ -413,8 +428,8 @@ async fn cmd_grpc_go<R: Runtime>(
},
&UpdateSource::Window,
)
.await
.unwrap();
.await
.unwrap();
});
}
Ok(IncomingMsg::Commit) => {
@@ -446,7 +461,7 @@ async fn cmd_grpc_go<R: Runtime>(
RenderPurpose::Send,
),
)
.await;
.await;
upsert_grpc_event(
&window,
@@ -458,8 +473,8 @@ async fn cmd_grpc_go<R: Runtime>(
},
&UpdateSource::Window,
)
.await
.unwrap();
.await
.unwrap();
async move {
let (maybe_stream, maybe_msg) =
@@ -497,8 +512,8 @@ async fn cmd_grpc_go<R: Runtime>(
},
&UpdateSource::Window,
)
.await
.unwrap();
.await
.unwrap();
}
match maybe_msg {
@@ -512,14 +527,14 @@ async fn cmd_grpc_go<R: Runtime>(
} else {
"Received response with metadata"
}
.to_string(),
.to_string(),
event_type: GrpcEventType::Info,
..base_event.clone()
},
&UpdateSource::Window,
)
.await
.unwrap();
.await
.unwrap();
upsert_grpc_event(
&window,
&GrpcEvent {
@@ -529,8 +544,8 @@ async fn cmd_grpc_go<R: Runtime>(
},
&UpdateSource::Window,
)
.await
.unwrap();
.await
.unwrap();
upsert_grpc_event(
&window,
&GrpcEvent {
@@ -541,8 +556,8 @@ async fn cmd_grpc_go<R: Runtime>(
},
&UpdateSource::Window,
)
.await
.unwrap();
.await
.unwrap();
}
Some(Err(e)) => {
upsert_grpc_event(
@@ -566,8 +581,8 @@ async fn cmd_grpc_go<R: Runtime>(
}),
&UpdateSource::Window,
)
.await
.unwrap();
.await
.unwrap();
}
None => {
// Server streaming doesn't return the initial message
@@ -585,14 +600,14 @@ async fn cmd_grpc_go<R: Runtime>(
} else {
"Received response with metadata"
}
.to_string(),
.to_string(),
event_type: GrpcEventType::Info,
..base_event.clone()
},
&UpdateSource::Window,
)
.await
.unwrap();
.await
.unwrap();
stream.into_inner()
}
Some(Err(e)) => {
@@ -618,8 +633,8 @@ async fn cmd_grpc_go<R: Runtime>(
}),
&UpdateSource::Window,
)
.await
.unwrap();
.await
.unwrap();
return;
}
None => return,
@@ -638,8 +653,8 @@ async fn cmd_grpc_go<R: Runtime>(
},
&UpdateSource::Window,
)
.await
.unwrap();
.await
.unwrap();
}
Ok(None) => {
let trailers =
@@ -655,8 +670,8 @@ async fn cmd_grpc_go<R: Runtime>(
},
&UpdateSource::Window,
)
.await
.unwrap();
.await
.unwrap();
break;
}
Err(status) => {
@@ -671,8 +686,8 @@ async fn cmd_grpc_go<R: Runtime>(
},
&UpdateSource::Window,
)
.await
.unwrap();
.await
.unwrap();
}
}
}
@@ -930,8 +945,8 @@ async fn cmd_import_data<R: Runtime>(
grpc_requests,
&UpdateSource::Import,
)
.await
.map_err(|e| e.to_string())?;
.await
.map_err(|e| e.to_string())?;
analytics::track_event(
&window,
@@ -939,7 +954,7 @@ async fn cmd_import_data<R: Runtime>(
AnalyticsAction::Import,
Some(json!({ "plugin": plugin_name })),
)
.await;
.await;
Ok(upserted)
}
@@ -960,6 +975,14 @@ async fn cmd_template_functions<R: Runtime>(
plugin_manager.get_template_functions(&window).await.map_err(|e| e.to_string())
}
#[tauri::command]
async fn cmd_get_http_authentication<R: Runtime>(
window: WebviewWindow<R>,
plugin_manager: State<'_, PluginManager>,
) -> Result<Vec<GetHttpAuthenticationResponse>, String> {
plugin_manager.get_http_authentication(&window).await.map_err(|e| e.to_string())
}
#[tauri::command]
async fn cmd_call_http_request_action<R: Runtime>(
window: WebviewWindow<R>,
@@ -985,7 +1008,7 @@ async fn cmd_curl_to_request<R: Runtime>(
AnalyticsAction::Import,
Some(json!({ "plugin": plugin_name })),
)
.await;
.await;
import_result.resources.http_requests.get(0).ok_or("No curl command found".to_string()).map(
|r| {
@@ -1170,8 +1193,8 @@ async fn cmd_install_plugin<R: Runtime>(
},
&UpdateSource::Window,
)
.await
.map_err(|e| e.to_string())?;
.await
.map_err(|e| e.to_string())?;
Ok(plugin)
}
@@ -1222,8 +1245,8 @@ async fn cmd_create_cookie_jar(
},
&UpdateSource::Window,
)
.await
.map_err(|e| e.to_string())
.await
.map_err(|e| e.to_string())
}
#[tauri::command]
@@ -1245,8 +1268,8 @@ async fn cmd_create_environment(
},
&UpdateSource::Window,
)
.await
.map_err(|e| e.to_string())
.await
.map_err(|e| e.to_string())
}
#[tauri::command]
@@ -1268,8 +1291,8 @@ async fn cmd_create_grpc_request(
},
&UpdateSource::Window,
)
.await
.map_err(|e| e.to_string())
.await
.map_err(|e| e.to_string())
}
#[tauri::command]
@@ -1508,8 +1531,8 @@ async fn cmd_list_cookie_jars(
},
&UpdateSource::Window,
)
.await
.expect("Failed to create CookieJar");
.await
.expect("Failed to create CookieJar");
Ok(vec![cookie_jar])
} else {
Ok(cookie_jars)
@@ -1598,8 +1621,8 @@ async fn cmd_list_workspaces(window: WebviewWindow) -> Result<Vec<Workspace>, St
},
&UpdateSource::Window,
)
.await
.expect("Failed to create Workspace");
.await
.expect("Failed to create Workspace");
Ok(vec![workspace])
} else {
Ok(workspaces)
@@ -1853,6 +1876,7 @@ pub fn run() {
cmd_get_environment,
cmd_get_folder,
cmd_get_grpc_request,
cmd_get_http_authentication,
cmd_get_http_request,
cmd_get_key_value,
cmd_get_settings,
@@ -1989,7 +2013,7 @@ fn create_main_window(handle: &AppHandle, url: &str) -> WebviewWindow {
Some(_) => counter += 1,
}
}
.expect("Failed to generate label for new window");
.expect("Failed to generate label for new window");
let config = CreateWindowConfig {
url,
@@ -2224,8 +2248,8 @@ async fn handle_plugin_event<R: Runtime>(
req.request_id.as_str(),
req.limit.map(|l| l as i64),
)
.await
.unwrap_or_default();
.await
.unwrap_or_default();
Some(InternalEventPayload::FindHttpResponsesResponse(FindHttpResponsesResponse {
http_responses,
}))
@@ -2254,7 +2278,7 @@ async fn handle_plugin_event<R: Runtime>(
environment.as_ref(),
&cb,
)
.await;
.await;
Some(InternalEventPayload::RenderHttpRequestResponse(RenderHttpRequestResponse {
http_request,
}))
@@ -2275,7 +2299,7 @@ async fn handle_plugin_event<R: Runtime>(
render_json_value(req.data, &base_environment, environment.as_ref(), &cb).await;
Some(InternalEventPayload::TemplateRenderResponse(TemplateRenderResponse { data }))
}
InternalEventPayload::ReloadResponse => {
InternalEventPayload::ReloadResponse(_) => {
let window = get_window_from_window_context(app_handle, &window_context)
.expect("Failed to find window for plugin reload");
let plugins = list_plugins(app_handle).await.unwrap();
@@ -2313,8 +2337,8 @@ async fn handle_plugin_event<R: Runtime>(
req.http_request.id.as_str(),
&UpdateSource::Plugin,
)
.await
.unwrap();
.await
.unwrap();
let result = send_http_request(
&window,
@@ -2324,7 +2348,7 @@ async fn handle_plugin_event<R: Runtime>(
cookie_jar,
&mut tokio::sync::watch::channel(false).1, // No-op cancel channel
)
.await;
.await;
let http_response = match result {
Ok(r) => r,

View File

@@ -312,7 +312,7 @@ mod placeholder_tests {
name: ":foo".into(),
value: "xxx".into(),
enabled: true,
id: "p1".into(),
id: None,
};
assert_eq!(
replace_path_placeholder(&p, "https://example.com/:foo/bar"),
@@ -326,7 +326,7 @@ mod placeholder_tests {
name: ":foo".into(),
value: "xxx".into(),
enabled: true,
id: "p1".into(),
id: None,
};
assert_eq!(
replace_path_placeholder(&p, "https://example.com/:foo"),
@@ -340,7 +340,7 @@ mod placeholder_tests {
name: ":foo".into(),
value: "xxx".into(),
enabled: true,
id: "p1".into(),
id: None,
};
assert_eq!(
replace_path_placeholder(&p, "https://example.com/:foo?:foo"),
@@ -354,7 +354,7 @@ mod placeholder_tests {
enabled: true,
name: "".to_string(),
value: "".to_string(),
id: "p1".into(),
id: None,
};
assert_eq!(
replace_path_placeholder(&p, "https://example.com/:missing"),
@@ -368,7 +368,7 @@ mod placeholder_tests {
enabled: false,
name: ":foo".to_string(),
value: "xxx".to_string(),
id: "p1".into(),
id: None,
};
assert_eq!(
replace_path_placeholder(&p, "https://example.com/:foo"),
@@ -382,7 +382,7 @@ mod placeholder_tests {
name: ":foo".into(),
value: "xxx".into(),
enabled: true,
id: "p1".into(),
id: None,
};
assert_eq!(
replace_path_placeholder(&p, "https://example.com/:foooo"),
@@ -396,7 +396,7 @@ mod placeholder_tests {
name: ":foo".into(),
value: "Hello World".into(),
enabled: true,
id: "p1".into(),
id: None,
};
assert_eq!(
replace_path_placeholder(&p, "https://example.com/:foo"),
@@ -413,13 +413,13 @@ mod placeholder_tests {
name: "b".to_string(),
value: "bbb".to_string(),
enabled: true,
id: "p1".into(),
id: None,
},
HttpUrlParameter {
name: ":a".to_string(),
value: "aaa".to_string(),
enabled: true,
id: "p2".into(),
id: None,
},
],
..Default::default()

View File

@@ -1,6 +1,6 @@
use std::collections::HashMap;
use tauri::{AppHandle, Manager, Runtime};
use yaak_plugins::events::{RenderPurpose, TemplateFunctionArg, WindowContext};
use yaak_plugins::events::{FormInput, RenderPurpose, WindowContext};
use yaak_plugins::manager::PluginManager;
use yaak_templates::TemplateCallback;
@@ -48,11 +48,11 @@ impl TemplateCallback for PluginTemplateCallback {
// Fill in default values for all args
for a_def in function.args {
let base = match a_def {
TemplateFunctionArg::Text(a) => a.base,
TemplateFunctionArg::Select(a) => a.base,
TemplateFunctionArg::Checkbox(a) => a.base,
TemplateFunctionArg::File(a) => a.base,
TemplateFunctionArg::HttpRequest(a) => a.base,
FormInput::Text(a) => a.base,
FormInput::Select(a) => a.base,
FormInput::Checkbox(a) => a.base,
FormInput::File(a) => a.base,
FormInput::HttpRequest(a) => a.base,
};
if let None = args_with_defaults.get(base.name.as_str()) {
args_with_defaults.insert(base.name, base.default_value.unwrap_or_default());

View File

@@ -0,0 +1,55 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
plugin: () => plugin
});
module.exports = __toCommonJS(src_exports);
var plugin = {
authentication: {
name: "Basic",
config: [{
type: "text",
name: "username",
label: "Username",
optional: true
}, {
type: "text",
name: "password",
label: "Password",
optional: true
}],
async onApply(_ctx, args) {
const { username, password } = args.config;
return {
url: args.url,
headers: [{
name: "Authorization",
value: "Basic " + Buffer.from(`${username}:${password}`).toString("base64")
}]
};
}
}
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
plugin
});

View File

@@ -0,0 +1,9 @@
{
"name": "@yaakapp/auth-basic",
"private": true,
"version": "0.0.1",
"scripts": {
"build": "yaakcli build ./src/index.ts",
"dev": "yaakcli dev ./src/index.js"
}
}

View File

@@ -0,0 +1,50 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
plugin: () => plugin
});
module.exports = __toCommonJS(src_exports);
var plugin = {
authentication: {
name: "Bearer",
config: [{
type: "text",
name: "token",
label: "Token",
optional: true
}],
async onApply(_ctx, args) {
const { token } = args.config;
return {
url: args.url,
headers: [{
name: "Authorization",
value: `Bearer ${token}`.trim()
}]
};
}
}
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
plugin
});

View File

@@ -0,0 +1,9 @@
{
"name": "@yaakapp/auth-bearer",
"private": true,
"version": "0.0.1",
"scripts": {
"build": "yaakcli build ./src/index.ts",
"dev": "yaakcli dev ./src/index.js"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,15 @@
{
"name": "@yaakapp/auth-jwt",
"private": true,
"version": "0.0.1",
"scripts": {
"build": "yaakcli build ./src/index.ts",
"dev": "yaakcli dev ./src/index.js"
},
"dependencies": {
"jsonwebtoken": "^9.0.2"
},
"devDependencies": {
"@types/jsonwebtoken": "^9.0.7"
}
}

View File

@@ -5,19 +5,20 @@ edition = "2021"
publish = false
[dependencies]
tonic = "0.10.2"
prost = "0.12"
tonic = "0.12.3"
prost = "0.13.4"
tokio = { version = "1.0", features = ["macros", "rt-multi-thread", "fs"] }
tonic-reflection = "0.10.2"
tonic-reflection = "0.12.3"
tokio-stream = "0.1.14"
prost-types = "0.12.3"
prost-types = "0.13.4"
serde = { version = "1.0.196", features = ["derive"] }
serde_json = "1.0.113"
prost-reflect = { version = "0.12.0", features = ["serde", "derive"] }
prost-reflect = { version = "0.14.4", features = ["serde", "derive"] }
log = "0.4.20"
anyhow = "1.0.79"
hyper = { version = "0.14" }
hyper-rustls = { version = "0.24.0", features = ["http2"] }
hyper = "1.5.2"
hyper-util = { version = "0.1.10", features = ["client-legacy", "client"] }
hyper-rustls = { version = "0.27.5", default-features = false, features = ["http2", "rustls-platform-verifier"] }
uuid = { version = "1.7.0", features = ["v4"] }
tauri = { workspace = true }
tauri-plugin-shell = { workspace = true }

View File

@@ -0,0 +1,172 @@
use crate::transport::get_transport;
use async_recursion::async_recursion;
use hyper_rustls::HttpsConnector;
use hyper_util::client::legacy::connect::HttpConnector;
use hyper_util::client::legacy::Client;
use log::debug;
use tokio_stream::StreamExt;
use tonic::body::BoxBody;
use tonic::transport::Uri;
use tonic::Request;
use tonic_reflection::pb::v1::server_reflection_request::MessageRequest;
use tonic_reflection::pb::v1::server_reflection_response::MessageResponse;
use tonic_reflection::pb::v1::{
ErrorResponse, ExtensionNumberResponse, ListServiceResponse, ServerReflectionRequest,
ServiceResponse,
};
use tonic_reflection::pb::v1::{ExtensionRequest, FileDescriptorResponse};
use tonic_reflection::pb::{v1, v1alpha};
pub struct AutoReflectionClient<T = Client<HttpsConnector<HttpConnector>, BoxBody>> {
use_v1alpha: bool,
client_v1: v1::server_reflection_client::ServerReflectionClient<T>,
client_v1alpha: v1alpha::server_reflection_client::ServerReflectionClient<T>,
}
impl AutoReflectionClient {
pub fn new(uri: &Uri) -> Self {
let client_v1 = v1::server_reflection_client::ServerReflectionClient::with_origin(
get_transport(),
uri.clone(),
);
let client_v1alpha = v1alpha::server_reflection_client::ServerReflectionClient::with_origin(
get_transport(),
uri.clone(),
);
AutoReflectionClient {
use_v1alpha: false,
client_v1,
client_v1alpha,
}
}
#[async_recursion]
pub async fn send_reflection_request(
&mut self,
message: MessageRequest,
) -> Result<MessageResponse, String> {
let reflection_request = ServerReflectionRequest {
host: "".into(), // Doesn't matter
message_request: Some(message.clone()),
};
if self.use_v1alpha {
let request = Request::new(tokio_stream::once(to_v1alpha_request(reflection_request)));
self.client_v1alpha
.server_reflection_info(request)
.await
.map_err(|e| match e.code() {
tonic::Code::Unavailable => "Failed to connect to endpoint".to_string(),
tonic::Code::Unauthenticated => "Authentication failed".to_string(),
tonic::Code::DeadlineExceeded => "Deadline exceeded".to_string(),
_ => e.to_string(),
})?
.into_inner()
.next()
.await
.expect("steamed response")
.map_err(|e| e.to_string())?
.message_response
.ok_or("No reflection response".to_string())
.map(|resp| to_v1_msg_response(resp))
} else {
let request = Request::new(tokio_stream::once(reflection_request));
let resp = self.client_v1.server_reflection_info(request).await;
match resp {
Ok(r) => Ok(r),
Err(e) => match e.code().clone() {
tonic::Code::Unimplemented => {
// If v1 fails, change to v1alpha and try again
debug!("gRPC schema reflection falling back to v1alpha");
self.use_v1alpha = true;
return self.send_reflection_request(message).await;
}
_ => Err(e),
},
}
.map_err(|e| match e.code() {
tonic::Code::Unavailable => "Failed to connect to endpoint".to_string(),
tonic::Code::Unauthenticated => "Authentication failed".to_string(),
tonic::Code::DeadlineExceeded => "Deadline exceeded".to_string(),
_ => e.to_string(),
})?
.into_inner()
.next()
.await
.expect("steamed response")
.map_err(|e| e.to_string())?
.message_response
.ok_or("No reflection response".to_string())
}
}
}
fn to_v1_msg_response(
response: v1alpha::server_reflection_response::MessageResponse,
) -> MessageResponse {
match response {
v1alpha::server_reflection_response::MessageResponse::FileDescriptorResponse(v) => {
MessageResponse::FileDescriptorResponse(FileDescriptorResponse {
file_descriptor_proto: v.file_descriptor_proto,
})
}
v1alpha::server_reflection_response::MessageResponse::AllExtensionNumbersResponse(v) => {
MessageResponse::AllExtensionNumbersResponse(ExtensionNumberResponse {
extension_number: v.extension_number,
base_type_name: v.base_type_name,
})
}
v1alpha::server_reflection_response::MessageResponse::ListServicesResponse(v) => {
MessageResponse::ListServicesResponse(ListServiceResponse {
service: v
.service
.iter()
.map(|s| ServiceResponse {
name: s.name.clone(),
})
.collect(),
})
}
v1alpha::server_reflection_response::MessageResponse::ErrorResponse(v) => {
MessageResponse::ErrorResponse(ErrorResponse {
error_code: v.error_code,
error_message: v.error_message,
})
}
}
}
fn to_v1alpha_request(request: ServerReflectionRequest) -> v1alpha::ServerReflectionRequest {
v1alpha::ServerReflectionRequest {
host: request.host,
message_request: request.message_request.map(|m| to_v1alpha_msg_request(m)),
}
}
fn to_v1alpha_msg_request(
message: MessageRequest,
) -> v1alpha::server_reflection_request::MessageRequest {
match message {
MessageRequest::FileByFilename(v) => {
v1alpha::server_reflection_request::MessageRequest::FileByFilename(v)
}
MessageRequest::FileContainingSymbol(v) => {
v1alpha::server_reflection_request::MessageRequest::FileContainingSymbol(v)
}
MessageRequest::FileContainingExtension(ExtensionRequest {
extension_number,
containing_type,
}) => v1alpha::server_reflection_request::MessageRequest::FileContainingExtension(
v1alpha::ExtensionRequest {
extension_number,
containing_type,
},
),
MessageRequest::AllExtensionNumbersOfType(v) => {
v1alpha::server_reflection_request::MessageRequest::AllExtensionNumbersOfType(v)
}
MessageRequest::ListServices(v) => {
v1alpha::server_reflection_request::MessageRequest::ListServices(v)
}
}
}

View File

@@ -5,7 +5,9 @@ use serde_json::Deserializer;
mod codec;
mod json_schema;
pub mod manager;
mod proto;
mod reflection;
mod transport;
mod client;
pub use tonic::metadata::*;
pub use tonic::Code;

View File

@@ -2,9 +2,9 @@ use std::collections::BTreeMap;
use std::path::PathBuf;
use std::str::FromStr;
use hyper::client::HttpConnector;
use hyper::Client;
use hyper_rustls::HttpsConnector;
use hyper_util::client::legacy::connect::HttpConnector;
use hyper_util::client::legacy::Client;
pub use prost_reflect::DynamicMessage;
use prost_reflect::{DescriptorPool, MethodDescriptor, ServiceDescriptor};
use serde_json::Deserializer;
@@ -16,10 +16,11 @@ use tonic::transport::Uri;
use tonic::{IntoRequest, IntoStreamingRequest, Request, Response, Status, Streaming};
use crate::codec::DynamicCodec;
use crate::proto::{
fill_pool_from_files, fill_pool_from_reflection, get_transport, method_desc_to_path,
use crate::reflection::{
fill_pool_from_files, fill_pool_from_reflection, method_desc_to_path,
};
use crate::{json_schema, MethodDefinition, ServiceDefinition};
use crate::transport::get_transport;
#[derive(Clone)]
pub struct GrpcConnection {

View File

@@ -3,11 +3,9 @@ use std::ops::Deref;
use std::path::PathBuf;
use std::str::FromStr;
use crate::client::AutoReflectionClient;
use anyhow::anyhow;
use async_recursion::async_recursion;
use hyper::client::HttpConnector;
use hyper::Client;
use hyper_rustls::{HttpsConnector, HttpsConnectorBuilder};
use log::{debug, warn};
use prost::Message;
use prost_reflect::{DescriptorPool, MethodDescriptor};
@@ -16,15 +14,10 @@ use tauri::path::BaseDirectory;
use tauri::{AppHandle, Manager};
use tauri_plugin_shell::ShellExt;
use tokio::fs;
use tokio_stream::StreamExt;
use tonic::body::BoxBody;
use tonic::codegen::http::uri::PathAndQuery;
use tonic::transport::Uri;
use tonic::Request;
use tonic_reflection::pb::server_reflection_client::ServerReflectionClient;
use tonic_reflection::pb::server_reflection_request::MessageRequest;
use tonic_reflection::pb::server_reflection_response::MessageResponse;
use tonic_reflection::pb::ServerReflectionRequest;
use tonic_reflection::pb::v1::server_reflection_request::MessageRequest;
use tonic_reflection::pb::v1::server_reflection_response::MessageResponse;
pub async fn fill_pool_from_files(
app_handle: &AppHandle,
@@ -98,7 +91,7 @@ pub async fn fill_pool_from_files(
pub async fn fill_pool_from_reflection(uri: &Uri) -> Result<DescriptorPool, String> {
let mut pool = DescriptorPool::new();
let mut client = ServerReflectionClient::with_origin(get_transport(), uri.clone());
let mut client = AutoReflectionClient::new(uri);
for service in list_services(&mut client).await? {
if service == "grpc.reflection.v1alpha.ServerReflection" {
@@ -114,21 +107,8 @@ pub async fn fill_pool_from_reflection(uri: &Uri) -> Result<DescriptorPool, Stri
Ok(pool)
}
pub fn get_transport() -> Client<HttpsConnector<HttpConnector>, BoxBody> {
let connector = HttpsConnectorBuilder::new().with_native_roots();
let connector = connector.https_or_http().enable_http2().wrap_connector({
let mut http_connector = HttpConnector::new();
http_connector.enforce_http(false);
http_connector
});
Client::builder().pool_max_idle_per_host(0).http2_only(true).build(connector)
}
async fn list_services(
reflect_client: &mut ServerReflectionClient<Client<HttpsConnector<HttpConnector>, BoxBody>>,
) -> Result<Vec<String>, String> {
let response =
send_reflection_request(reflect_client, MessageRequest::ListServices("".into())).await?;
async fn list_services(client: &mut AutoReflectionClient) -> Result<Vec<String>, String> {
let response = client.send_reflection_request(MessageRequest::ListServices("".into())).await?;
let list_services_response = match response {
MessageResponse::ListServicesResponse(resp) => resp,
@@ -141,13 +121,11 @@ async fn list_services(
async fn file_descriptor_set_from_service_name(
service_name: &str,
pool: &mut DescriptorPool,
client: &mut ServerReflectionClient<Client<HttpsConnector<HttpConnector>, BoxBody>>,
client: &mut AutoReflectionClient,
) {
let response = match send_reflection_request(
client,
MessageRequest::FileContainingSymbol(service_name.into()),
)
.await
let response = match client
.send_reflection_request(MessageRequest::FileContainingSymbol(service_name.into()))
.await
{
Ok(resp) => resp,
Err(e) => {
@@ -169,7 +147,7 @@ async fn file_descriptor_set_from_service_name(
async fn add_file_descriptors_to_pool(
fds: Vec<Vec<u8>>,
pool: &mut DescriptorPool,
client: &mut ServerReflectionClient<Client<HttpsConnector<HttpConnector>, BoxBody>>,
client: &mut AutoReflectionClient,
) {
let mut topo_sort = topology::SimpleTopoSort::new();
let mut fd_mapping = std::collections::HashMap::with_capacity(fds.len());
@@ -198,15 +176,15 @@ async fn add_file_descriptors_to_pool(
async fn file_descriptor_set_by_filename(
filename: &str,
pool: &mut DescriptorPool,
client: &mut ServerReflectionClient<Client<HttpsConnector<HttpConnector>, BoxBody>>,
client: &mut AutoReflectionClient,
) {
// We already fetched this file
if let Some(_) = pool.get_file_by_name(filename) {
return;
}
let response =
send_reflection_request(client, MessageRequest::FileByFilename(filename.into())).await;
let msg = MessageRequest::FileByFilename(filename.into());
let response = client.send_reflection_request(msg).await;
let file_descriptor_response = match response {
Ok(MessageResponse::FileDescriptorResponse(resp)) => resp,
Ok(_) => {
@@ -222,35 +200,6 @@ async fn file_descriptor_set_by_filename(
.await;
}
async fn send_reflection_request(
client: &mut ServerReflectionClient<Client<HttpsConnector<HttpConnector>, BoxBody>>,
message: MessageRequest,
) -> Result<MessageResponse, String> {
let reflection_request = ServerReflectionRequest {
host: "".into(), // Doesn't matter
message_request: Some(message),
};
let request = Request::new(tokio_stream::once(reflection_request));
client
.server_reflection_info(request)
.await
.map_err(|e| match e.code() {
tonic::Code::Unavailable => "Failed to connect to endpoint".to_string(),
tonic::Code::Unauthenticated => "Authentication failed".to_string(),
tonic::Code::DeadlineExceeded => "Deadline exceeded".to_string(),
_ => e.to_string(),
})?
.into_inner()
.next()
.await
.expect("steamed response")
.map_err(|e| e.to_string())?
.message_response
.ok_or("No reflection response".to_string())
}
pub fn method_desc_to_path(md: &MethodDescriptor) -> PathAndQuery {
let full_name = md.full_name();
let (namespace, method_name) = full_name
@@ -292,8 +241,8 @@ mod topology {
where
T: Eq + std::hash::Hash + Clone,
{
type IntoIter = SimpleTopoSortIter<T>;
type Item = <SimpleTopoSortIter<T> as Iterator>::Item;
type IntoIter = SimpleTopoSortIter<T>;
fn into_iter(self) -> Self::IntoIter {
SimpleTopoSortIter::new(self)

View File

@@ -0,0 +1,19 @@
use hyper_rustls::{HttpsConnector, HttpsConnectorBuilder};
use hyper_util::client::legacy::Client;
use hyper_util::client::legacy::connect::HttpConnector;
use hyper_util::rt::TokioExecutor;
use tonic::body::BoxBody;
pub(crate) fn get_transport() -> Client<HttpsConnector<HttpConnector>, BoxBody> {
let connector = HttpsConnectorBuilder::new().with_platform_verifier();
let connector = connector.https_or_http().enable_http2().wrap_connector({
let mut http_connector = HttpConnector::new();
http_connector.enforce_http(false);
http_connector
});
Client::builder(TokioExecutor::new())
.pool_max_idle_per_host(0)
.http2_only(true)
.build(connector)
}

View File

@@ -26,13 +26,13 @@ export type GrpcEvent = { model: "grpc_event", id: string, createdAt: string, up
export type GrpcEventType = "info" | "error" | "client_message" | "server_message" | "connection_start" | "connection_end";
export type GrpcMetadataEntry = { enabled?: boolean, name: string, value: string, id: string, };
export type GrpcMetadataEntry = { enabled?: boolean, name: string, value: string, id?: string, };
export type GrpcRequest = { model: "grpc_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authenticationType: string | null, authentication: Record<string, any>, description: string, message: string, metadata: Array<GrpcMetadataEntry>, method: string | null, name: string, service: string | null, sortPriority: number, url: string, };
export type HttpRequest = { model: "http_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authentication: Record<string, any>, authenticationType: string | null, body: Record<string, any>, bodyType: string | null, description: string, headers: Array<HttpRequestHeader>, method: string, name: string, sortPriority: number, url: string, urlParameters: Array<HttpUrlParameter>, };
export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, id: string, };
export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, id?: string, };
export type HttpResponse = { model: "http_response", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, bodyPath: string | null, contentLength: number | null, elapsed: number, elapsedHeaders: number, error: string | null, headers: Array<HttpResponseHeader>, remoteAddr: string | null, status: number, statusReason: string | null, state: HttpResponseState, url: string, version: string | null, };
@@ -40,7 +40,7 @@ export type HttpResponseHeader = { name: string, value: string, };
export type HttpResponseState = "initialized" | "connected" | "closed";
export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, id: string, };
export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, id?: string, };
export type KeyValue = { model: "key_value", createdAt: string, updatedAt: string, key: string, namespace: string, value: string, };

View File

@@ -446,7 +446,8 @@ pub struct HttpRequestHeader {
pub enabled: bool,
pub name: String,
pub value: String,
pub id: String,
#[ts(optional, as = "Option<String>")]
pub id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
@@ -458,7 +459,8 @@ pub struct HttpUrlParameter {
pub enabled: bool,
pub name: String,
pub value: String,
pub id: String,
#[ts(optional, as = "Option<String>")]
pub id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
@@ -664,7 +666,8 @@ pub struct GrpcMetadataEntry {
pub enabled: bool,
pub name: String,
pub value: String,
pub id: String,
#[ts(optional, as = "Option<String>")]
pub id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]

View File

@@ -13,9 +13,9 @@ serde = { version = "1.0.198", features = ["derive"] }
serde_json = "1.0.113"
tauri = { workspace = true }
tauri-plugin-shell = { workspace = true }
tokio = { version = "1.0", features = ["macros", "rt-multi-thread", "process"] }
tonic = "0.12.1"
ts-rs = "10.0.0"
tokio = { version = "1.42.0", features = ["macros", "rt-multi-thread", "process"] }
tonic = { version = "0.12.3"}
ts-rs = { workspace = true }
thiserror = "2.0.7"
yaak-models = { workspace = true }
regex = "1.10.6"

View File

@@ -11,6 +11,10 @@ export type BootRequest = { dir: string, watch: boolean, };
export type BootResponse = { name: string, version: string, capabilities: Array<string>, };
export type CallHttpAuthenticationRequest = { config: { [key in string]?: JsonValue }, method: string, url: string, headers: Array<HttpHeader>, };
export type CallHttpAuthenticationResponse = { url: string, headers: Array<HttpHeader>, };
export type CallHttpRequestActionArgs = { httpRequest: HttpRequest, };
export type CallHttpRequestActionRequest = { key: string, pluginRefId: string, args: CallHttpRequestActionArgs, };
@@ -25,10 +29,18 @@ export type Color = "custom" | "default" | "primary" | "secondary" | "info" | "s
export type CopyTextRequest = { text: string, };
export type EmptyPayload = {};
export type ExportHttpRequestRequest = { httpRequest: HttpRequest, };
export type ExportHttpRequestResponse = { content: string, };
export type FileFilter = { name: string,
/**
* File extensions to require
*/
extensions: Array<string>, };
export type FilterRequest = { content: string, filter: string, };
export type FilterResponse = { content: string, };
@@ -37,6 +49,112 @@ export type FindHttpResponsesRequest = { requestId: string, limit?: number, };
export type FindHttpResponsesResponse = { httpResponses: Array<HttpResponse>, };
export type FormInput = { "type": "text" } & FormInputText | { "type": "select" } & FormInputSelect | { "type": "checkbox" } & FormInputCheckbox | { "type": "file" } & FormInputFile | { "type": "http_request" } & FormInputHttpRequest;
export type FormInputBase = { name: string,
/**
* Whether the user must fill in the argument
*/
optional?: boolean,
/**
* The label of the input
*/
label?: string,
/**
* The default value
*/
defaultValue?: string, };
export type FormInputCheckbox = { name: string,
/**
* Whether the user must fill in the argument
*/
optional?: boolean,
/**
* The label of the input
*/
label?: string,
/**
* The default value
*/
defaultValue?: string, };
export type FormInputFile = {
/**
* The title of the file selection window
*/
title: string,
/**
* Allow selecting multiple files
*/
multiple?: boolean, directory?: boolean, defaultPath?: string, filters?: Array<FileFilter>, name: string,
/**
* Whether the user must fill in the argument
*/
optional?: boolean,
/**
* The label of the input
*/
label?: string,
/**
* The default value
*/
defaultValue?: string, };
export type FormInputHttpRequest = { name: string,
/**
* Whether the user must fill in the argument
*/
optional?: boolean,
/**
* The label of the input
*/
label?: string,
/**
* The default value
*/
defaultValue?: string, };
export type FormInputSelect = {
/**
* The options that will be available in the select input
*/
options: Array<FormInputSelectOption>, name: string,
/**
* Whether the user must fill in the argument
*/
optional?: boolean,
/**
* The label of the input
*/
label?: string,
/**
* The default value
*/
defaultValue?: string, };
export type FormInputSelectOption = { name: string, value: string, };
export type FormInputText = {
/**
* Placeholder for the text input
*/
placeholder?: string | null, name: string,
/**
* Whether the user must fill in the argument
*/
optional?: boolean,
/**
* The label of the input
*/
label?: string,
/**
* The default value
*/
defaultValue?: string, };
export type GetHttpAuthenticationResponse = { name: string, pluginName: string, config: Array<FormInput>, };
export type GetHttpRequestActionsRequest = Record<string, never>;
export type GetHttpRequestActionsResponse = { actions: Array<HttpRequestAction>, pluginRefId: string, };
@@ -47,6 +165,8 @@ export type GetHttpRequestByIdResponse = { httpRequest: HttpRequest | null, };
export type GetTemplateFunctionsResponse = { functions: Array<TemplateFunction>, pluginRefId: string, };
export type HttpHeader = { name: string, value: string, };
export type HttpRequestAction = { key: string, label: string, icon?: Icon, };
export type Icon = "copy" | "info" | "check_circle" | "alert_triangle" | "_unknown";
@@ -59,13 +179,7 @@ export type ImportResponse = { resources: ImportResources, };
export type InternalEvent = { id: string, pluginRefId: string, replyId: string | null, payload: InternalEventPayload, windowContext: WindowContext, };
export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } & BootResponse | { "type": "reload_request" } | { "type": "reload_response" } | { "type": "terminate_request" } | { "type": "terminate_response" } | { "type": "import_request" } & ImportRequest | { "type": "import_response" } & ImportResponse | { "type": "filter_request" } & FilterRequest | { "type": "filter_response" } & FilterResponse | { "type": "export_http_request_request" } & ExportHttpRequestRequest | { "type": "export_http_request_response" } & ExportHttpRequestResponse | { "type": "send_http_request_request" } & SendHttpRequestRequest | { "type": "send_http_request_response" } & SendHttpRequestResponse | { "type": "get_http_request_actions_request" } & GetHttpRequestActionsRequest | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_template_functions_request" } | { "type": "get_template_functions_response" } & GetTemplateFunctionsResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "copy_text_request" } & CopyTextRequest | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "show_toast_request" } & ShowToastRequest | { "type": "prompt_text_request" } & PromptTextRequest | { "type": "prompt_text_response" } & PromptTextResponse | { "type": "get_http_request_by_id_request" } & GetHttpRequestByIdRequest | { "type": "get_http_request_by_id_response" } & GetHttpRequestByIdResponse | { "type": "find_http_responses_request" } & FindHttpResponsesRequest | { "type": "find_http_responses_response" } & FindHttpResponsesResponse | { "type": "empty_response" };
export type OpenFileFilter = { name: string,
/**
* File extensions to require
*/
extensions: Array<string>, };
export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } & BootResponse | { "type": "reload_request" } & EmptyPayload | { "type": "reload_response" } & EmptyPayload | { "type": "terminate_request" } | { "type": "terminate_response" } | { "type": "import_request" } & ImportRequest | { "type": "import_response" } & ImportResponse | { "type": "filter_request" } & FilterRequest | { "type": "filter_response" } & FilterResponse | { "type": "export_http_request_request" } & ExportHttpRequestRequest | { "type": "export_http_request_response" } & ExportHttpRequestResponse | { "type": "send_http_request_request" } & SendHttpRequestRequest | { "type": "send_http_request_response" } & SendHttpRequestResponse | { "type": "get_http_request_actions_request" } & EmptyPayload | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_template_functions_request" } | { "type": "get_template_functions_response" } & GetTemplateFunctionsResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "get_http_authentication_request" } & EmptyPayload | { "type": "get_http_authentication_response" } & GetHttpAuthenticationResponse | { "type": "call_http_authentication_request" } & CallHttpAuthenticationRequest | { "type": "call_http_authentication_response" } & CallHttpAuthenticationResponse | { "type": "copy_text_request" } & CopyTextRequest | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "show_toast_request" } & ShowToastRequest | { "type": "prompt_text_request" } & PromptTextRequest | { "type": "prompt_text_response" } & PromptTextResponse | { "type": "get_http_request_by_id_request" } & GetHttpRequestByIdRequest | { "type": "get_http_request_by_id_response" } & GetHttpRequestByIdResponse | { "type": "find_http_responses_request" } & FindHttpResponsesRequest | { "type": "find_http_responses_response" } & FindHttpResponsesResponse | { "type": "empty_response" } & EmptyPayload;
export type PromptTextRequest = { id: string, title: string, label: string, description?: string, defaultValue?: string, placeholder?: string,
/**
@@ -100,135 +214,7 @@ export type TemplateFunction = { name: string, description?: string,
* Also support alternative names. This is useful for not breaking existing
* tags when changing the `name` property
*/
aliases?: Array<string>, args: Array<TemplateFunctionArg>, };
export type TemplateFunctionArg = { "type": "text" } & TemplateFunctionTextArg | { "type": "select" } & TemplateFunctionSelectArg | { "type": "checkbox" } & TemplateFunctionCheckboxArg | { "type": "http_request" } & TemplateFunctionHttpRequestArg | { "type": "file" } & TemplateFunctionFileArg;
export type TemplateFunctionBaseArg = {
/**
* The name of the argument. Should be `camelCase` format
*/
name: string,
/**
* Whether the user must fill in the argument
*/
optional?: boolean,
/**
* The label of the input
*/
label?: string,
/**
* The default value
*/
defaultValue?: string, };
export type TemplateFunctionCheckboxArg = {
/**
* The name of the argument. Should be `camelCase` format
*/
name: string,
/**
* Whether the user must fill in the argument
*/
optional?: boolean,
/**
* The label of the input
*/
label?: string,
/**
* The default value
*/
defaultValue?: string, };
export type TemplateFunctionFileArg = {
/**
* The title of the file selection window
*/
title: string,
/**
* Allow selecting multiple files
*/
multiple?: boolean, directory?: boolean, defaultPath?: string, filters?: Array<OpenFileFilter>,
/**
* The name of the argument. Should be `camelCase` format
*/
name: string,
/**
* Whether the user must fill in the argument
*/
optional?: boolean,
/**
* The label of the input
*/
label?: string,
/**
* The default value
*/
defaultValue?: string, };
export type TemplateFunctionHttpRequestArg = {
/**
* The name of the argument. Should be `camelCase` format
*/
name: string,
/**
* Whether the user must fill in the argument
*/
optional?: boolean,
/**
* The label of the input
*/
label?: string,
/**
* The default value
*/
defaultValue?: string, };
export type TemplateFunctionSelectArg = {
/**
* The options that will be available in the select input
*/
options: Array<TemplateFunctionSelectOption>,
/**
* The name of the argument. Should be `camelCase` format
*/
name: string,
/**
* Whether the user must fill in the argument
*/
optional?: boolean,
/**
* The label of the input
*/
label?: string,
/**
* The default value
*/
defaultValue?: string, };
export type TemplateFunctionSelectOption = { label: string, value: string, };
export type TemplateFunctionTextArg = {
/**
* Placeholder for the text input
*/
placeholder?: string,
/**
* The name of the argument. Should be `camelCase` format
*/
name: string,
/**
* Whether the user must fill in the argument
*/
optional?: boolean,
/**
* The label of the input
*/
label?: string,
/**
* The default value
*/
defaultValue?: string, };
aliases?: Array<string>, args: Array<FormInput>, };
export type TemplateRenderRequest = { data: JsonValue, purpose: RenderPurpose, };

View File

@@ -6,13 +6,13 @@ export type EnvironmentVariable = { enabled?: boolean, name: string, value: stri
export type Folder = { model: "folder", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, name: string, description: string, sortPriority: number, };
export type GrpcMetadataEntry = { enabled?: boolean, name: string, value: string, id: string, };
export type GrpcMetadataEntry = { enabled?: boolean, name: string, value: string, id?: string, };
export type GrpcRequest = { model: "grpc_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authenticationType: string | null, authentication: Record<string, any>, description: string, message: string, metadata: Array<GrpcMetadataEntry>, method: string | null, name: string, service: string | null, sortPriority: number, url: string, };
export type HttpRequest = { model: "http_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authentication: Record<string, any>, authenticationType: string | null, body: Record<string, any>, bodyType: string | null, description: string, headers: Array<HttpRequestHeader>, method: string, name: string, sortPriority: number, url: string, urlParameters: Array<HttpUrlParameter>, };
export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, id: string, };
export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, id?: string, };
export type HttpResponse = { model: "http_response", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, bodyPath: string | null, contentLength: number | null, elapsed: number, elapsedHeaders: number, error: string | null, headers: Array<HttpResponseHeader>, remoteAddr: string | null, status: number, statusReason: string | null, state: HttpResponseState, url: string, version: string | null, };
@@ -20,6 +20,6 @@ export type HttpResponseHeader = { name: string, value: string, };
export type HttpResponseState = "initialized" | "connected" | "closed";
export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, id: string, };
export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, id?: string, };
export type Workspace = { model: "workspace", id: string, createdAt: string, updatedAt: string, name: string, description: string, settingValidateCertificates: boolean, settingFollowRedirects: boolean, settingRequestTimeout: number, };

View File

@@ -39,8 +39,8 @@ pub enum InternalEventPayload {
BootRequest(BootRequest),
BootResponse(BootResponse),
ReloadRequest,
ReloadResponse,
ReloadRequest(EmptyPayload),
ReloadResponse(EmptyPayload),
TerminateRequest,
TerminateResponse,
@@ -57,15 +57,22 @@ pub enum InternalEventPayload {
SendHttpRequestRequest(SendHttpRequestRequest),
SendHttpRequestResponse(SendHttpRequestResponse),
GetHttpRequestActionsRequest(GetHttpRequestActionsRequest),
// Request Actions
GetHttpRequestActionsRequest(EmptyPayload),
GetHttpRequestActionsResponse(GetHttpRequestActionsResponse),
CallHttpRequestActionRequest(CallHttpRequestActionRequest),
// Template Functions
GetTemplateFunctionsRequest,
GetTemplateFunctionsResponse(GetTemplateFunctionsResponse),
CallTemplateFunctionRequest(CallTemplateFunctionRequest),
CallTemplateFunctionResponse(CallTemplateFunctionResponse),
GetHttpAuthenticationRequest(EmptyPayload),
GetHttpAuthenticationResponse(GetHttpAuthenticationResponse),
CallHttpAuthenticationRequest(CallHttpAuthenticationRequest),
CallHttpAuthenticationResponse(CallHttpAuthenticationResponse),
CopyTextRequest(CopyTextRequest),
RenderHttpRequestRequest(RenderHttpRequestRequest),
@@ -87,9 +94,14 @@ pub enum InternalEventPayload {
/// Returned when a plugin doesn't get run, just so the server
/// has something to listen for
EmptyResponse,
EmptyResponse(EmptyPayload),
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
#[serde(default)]
#[ts(export, type = "{}", export_to = "events.ts")]
pub struct EmptyPayload {}
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
#[serde(default, rename_all = "camelCase")]
#[ts(export, export_to = "events.ts")]
@@ -281,6 +293,41 @@ pub enum Icon {
_Unknown(String),
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
#[serde(default, rename_all = "camelCase")]
#[ts(export, export_to = "events.ts")]
pub struct GetHttpAuthenticationResponse {
pub name: String,
pub plugin_name: String,
pub config: Vec<FormInput>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
#[serde(default, rename_all = "camelCase")]
#[ts(export, export_to = "events.ts")]
pub struct CallHttpAuthenticationRequest {
pub config: serde_json::Map<String, serde_json::Value>,
pub method: String,
pub url: String,
pub headers: Vec<HttpHeader>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
#[serde(default, rename_all = "camelCase")]
#[ts(export, export_to = "events.ts")]
pub struct HttpHeader {
pub name: String,
pub value: String,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
#[serde(default, rename_all = "camelCase")]
#[ts(export, export_to = "events.ts")]
pub struct CallHttpAuthenticationResponse {
pub url: String,
pub headers: Vec<HttpHeader>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
#[serde(default, rename_all = "camelCase")]
#[ts(export, export_to = "events.ts")]
@@ -301,25 +348,24 @@ pub struct TemplateFunction {
/// tags when changing the `name` property
#[ts(optional)]
pub aliases: Option<Vec<String>>,
pub args: Vec<TemplateFunctionArg>,
pub args: Vec<FormInput>,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[serde(rename_all = "snake_case", tag = "type")]
#[ts(export, export_to = "events.ts")]
pub enum TemplateFunctionArg {
Text(TemplateFunctionTextArg),
Select(TemplateFunctionSelectArg),
Checkbox(TemplateFunctionCheckboxArg),
HttpRequest(TemplateFunctionHttpRequestArg),
File(TemplateFunctionFileArg),
pub enum FormInput {
Text(FormInputText),
Select(FormInputSelect),
Checkbox(FormInputCheckbox),
File(FormInputFile),
HttpRequest(FormInputHttpRequest),
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
#[serde(default, rename_all = "camelCase")]
#[ts(export, export_to = "events.ts")]
pub struct TemplateFunctionBaseArg {
/// The name of the argument. Should be `camelCase` format
pub struct FormInputBase {
pub name: String,
/// Whether the user must fill in the argument
@@ -338,29 +384,29 @@ pub struct TemplateFunctionBaseArg {
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
#[serde(default, rename_all = "camelCase")]
#[ts(export, export_to = "events.ts")]
pub struct TemplateFunctionTextArg {
pub struct FormInputText {
#[serde(flatten)]
pub base: TemplateFunctionBaseArg,
pub base: FormInputBase,
/// Placeholder for the text input
#[ts(optional)]
#[ts(optional = nullable)]
pub placeholder: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
#[serde(default, rename_all = "camelCase")]
#[ts(export, export_to = "events.ts")]
pub struct TemplateFunctionHttpRequestArg {
pub struct FormInputHttpRequest {
#[serde(flatten)]
pub base: TemplateFunctionBaseArg,
pub base: FormInputBase,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
#[serde(default, rename_all = "camelCase")]
#[ts(export, export_to = "events.ts")]
pub struct TemplateFunctionFileArg {
pub struct FormInputFile {
#[serde(flatten)]
pub base: TemplateFunctionBaseArg,
pub base: FormInputBase,
/// The title of the file selection window
pub title: String,
@@ -379,13 +425,13 @@ pub struct TemplateFunctionFileArg {
// Specify to only allow selection of certain file extensions
#[ts(optional)]
pub filters: Option<Vec<OpenFileFilter>>,
pub filters: Option<Vec<FileFilter>>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
#[serde(default, rename_all = "camelCase")]
#[ts(export, export_to = "events.ts")]
pub struct OpenFileFilter {
pub struct FileFilter {
pub name: String,
/// File extensions to require
pub extensions: Vec<String>,
@@ -394,27 +440,27 @@ pub struct OpenFileFilter {
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
#[serde(default, rename_all = "camelCase")]
#[ts(export, export_to = "events.ts")]
pub struct TemplateFunctionSelectArg {
pub struct FormInputSelect {
#[serde(flatten)]
pub base: TemplateFunctionBaseArg,
pub base: FormInputBase,
/// The options that will be available in the select input
pub options: Vec<TemplateFunctionSelectOption>,
pub options: Vec<FormInputSelectOption>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
#[serde(default, rename_all = "camelCase")]
#[ts(export, export_to = "events.ts")]
pub struct TemplateFunctionCheckboxArg {
pub struct FormInputCheckbox {
#[serde(flatten)]
pub base: TemplateFunctionBaseArg,
pub base: FormInputBase,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
#[serde(default, rename_all = "camelCase")]
#[ts(export, export_to = "events.ts")]
pub struct TemplateFunctionSelectOption {
pub label: String,
pub struct FormInputSelectOption {
pub name: String,
pub value: String,
}

View File

@@ -1,9 +1,10 @@
use crate::error::Error::{ClientNotInitializedErr, PluginErr, PluginNotFoundErr, UnknownEventErr};
use crate::error::Result;
use crate::events::{
BootRequest, CallHttpRequestActionRequest, CallTemplateFunctionArgs,
CallTemplateFunctionRequest, CallTemplateFunctionResponse, FilterRequest, FilterResponse,
GetHttpRequestActionsRequest, GetHttpRequestActionsResponse, GetTemplateFunctionsResponse,
BootRequest, CallHttpAuthenticationRequest, CallHttpAuthenticationResponse,
CallHttpRequestActionRequest, CallTemplateFunctionArgs, CallTemplateFunctionRequest,
CallTemplateFunctionResponse, EmptyPayload, FilterRequest, FilterResponse,
GetHttpAuthenticationResponse, GetHttpRequestActionsResponse, GetTemplateFunctionsResponse,
ImportRequest, ImportResponse, InternalEvent, InternalEventPayload, RenderPurpose,
WindowContext,
};
@@ -402,9 +403,7 @@ impl PluginManager {
let reply_events = self
.send_and_wait(
WindowContext::from_window(window),
&InternalEventPayload::GetHttpRequestActionsRequest(
GetHttpRequestActionsRequest {},
),
&InternalEventPayload::GetHttpRequestActionsRequest(EmptyPayload {}),
)
.await?;
@@ -460,6 +459,54 @@ impl PluginManager {
Ok(())
}
pub async fn get_http_authentication<R: Runtime>(
&self,
window: &WebviewWindow<R>,
) -> Result<Vec<GetHttpAuthenticationResponse>> {
let window_context = WindowContext::from_window(window);
let reply_events = self
.send_and_wait(
window_context,
&InternalEventPayload::GetHttpAuthenticationRequest(EmptyPayload {}),
)
.await?;
let mut results = Vec::new();
for event in reply_events {
if let InternalEventPayload::GetHttpAuthenticationResponse(resp) = event.payload {
results.push(resp.clone());
}
}
Ok(results)
}
pub async fn call_http_authentication<R: Runtime>(
&self,
window: &WebviewWindow<R>,
plugin_name: &str,
req: CallHttpAuthenticationRequest,
) -> Result<CallHttpAuthenticationResponse> {
let plugin = self
.get_plugin_by_name(plugin_name)
.await
.ok_or(PluginNotFoundErr(plugin_name.to_string()))?;
let event = self
.send_to_plugin_and_wait(
WindowContext::from_window(window),
&plugin,
&InternalEventPayload::CallHttpAuthenticationRequest(req),
)
.await?;
match event.payload {
InternalEventPayload::CallHttpAuthenticationResponse(resp) => Ok(resp),
InternalEventPayload::EmptyResponse(_) => {
Err(PluginErr("Auth plugin returned empty".to_string()))
}
e => Err(PluginErr(format!("Auth plugin returned invalid event {:?}", e))),
}
}
pub async fn call_template_function(
&self,
window_context: WindowContext,
@@ -552,7 +599,7 @@ impl PluginManager {
match event.payload {
InternalEventPayload::FilterResponse(resp) => Ok(resp),
InternalEventPayload::EmptyResponse => {
InternalEventPayload::EmptyResponse(_) => {
Err(PluginErr("Filter returned empty".to_string()))
}
e => Err(PluginErr(format!("Export returned invalid event {:?}", e))),

View File

@@ -6,15 +6,15 @@ export type EnvironmentVariable = { enabled?: boolean, name: string, value: stri
export type Folder = { model: "folder", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, name: string, description: string, sortPriority: number, };
export type GrpcMetadataEntry = { enabled?: boolean, name: string, value: string, id: string, };
export type GrpcMetadataEntry = { enabled?: boolean, name: string, value: string, id?: string, };
export type GrpcRequest = { model: "grpc_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authenticationType: string | null, authentication: Record<string, any>, description: string, message: string, metadata: Array<GrpcMetadataEntry>, method: string | null, name: string, service: string | null, sortPriority: number, url: string, };
export type HttpRequest = { model: "http_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authentication: Record<string, any>, authenticationType: string | null, body: Record<string, any>, bodyType: string | null, description: string, headers: Array<HttpRequestHeader>, method: string, name: string, sortPriority: number, url: string, urlParameters: Array<HttpUrlParameter>, };
export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, id: string, };
export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, id?: string, };
export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, id: string, };
export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, id?: string, };
export type SyncModel = { "type": "workspace" } & Workspace | { "type": "environment" } & Environment | { "type": "folder" } & Folder | { "type": "http_request" } & HttpRequest | { "type": "grpc_request" } & GrpcRequest;