mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-05-25 08:59:46 +02:00
OAuth 2 (#158)
This commit is contained in:
Generated
+30
-31
@@ -712,7 +712,7 @@ dependencies = [
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.7",
|
||||
"thiserror 2.0.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5212,8 +5212,6 @@ dependencies = [
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"sqlx-core",
|
||||
"sqlx-mysql",
|
||||
"sqlx-postgres",
|
||||
"sqlx-sqlite",
|
||||
"syn 2.0.87",
|
||||
"tempfile",
|
||||
@@ -5252,7 +5250,6 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
"rand 0.8.5",
|
||||
"rsa",
|
||||
"serde",
|
||||
"sha1",
|
||||
"sha2",
|
||||
"smallvec",
|
||||
@@ -5556,9 +5553,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
||||
|
||||
[[package]]
|
||||
name = "tauri"
|
||||
version = "2.2.0"
|
||||
version = "2.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e2e3349fbb2be7af9fad1b43d61ac83ba55ab48d47fbe1b2732f0c8211610a9"
|
||||
checksum = "78f6efc261c7905839b4914889a5b25df07f0ff89c63fb4afd6ff8c96af15e4d"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -5594,7 +5591,7 @@ dependencies = [
|
||||
"tauri-runtime",
|
||||
"tauri-runtime-wry",
|
||||
"tauri-utils",
|
||||
"thiserror 2.0.7",
|
||||
"thiserror 2.0.11",
|
||||
"tokio",
|
||||
"tray-icon",
|
||||
"url",
|
||||
@@ -5647,7 +5644,7 @@ dependencies = [
|
||||
"sha2",
|
||||
"syn 2.0.87",
|
||||
"tauri-utils",
|
||||
"thiserror 2.0.7",
|
||||
"thiserror 2.0.11",
|
||||
"time",
|
||||
"url",
|
||||
"uuid",
|
||||
@@ -5697,7 +5694,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.7",
|
||||
"thiserror 2.0.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5714,7 +5711,7 @@ dependencies = [
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"tauri-plugin-fs",
|
||||
"thiserror 2.0.7",
|
||||
"thiserror 2.0.11",
|
||||
"url",
|
||||
]
|
||||
|
||||
@@ -5735,7 +5732,7 @@ dependencies = [
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"tauri-utils",
|
||||
"thiserror 2.0.7",
|
||||
"thiserror 2.0.11",
|
||||
"toml 0.8.19",
|
||||
"url",
|
||||
"uuid",
|
||||
@@ -5759,7 +5756,7 @@ dependencies = [
|
||||
"swift-rs",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.7",
|
||||
"thiserror 2.0.11",
|
||||
"time",
|
||||
]
|
||||
|
||||
@@ -5779,7 +5776,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.7",
|
||||
"thiserror 2.0.11",
|
||||
"url",
|
||||
"windows",
|
||||
"zbus 5.3.0",
|
||||
@@ -5800,7 +5797,7 @@ dependencies = [
|
||||
"sys-locale",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.7",
|
||||
"thiserror 2.0.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5820,7 +5817,7 @@ dependencies = [
|
||||
"shared_child",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.7",
|
||||
"thiserror 2.0.11",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
@@ -5833,7 +5830,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"thiserror 2.0.7",
|
||||
"thiserror 2.0.11",
|
||||
"tracing",
|
||||
"windows-sys 0.59.0",
|
||||
"zbus 5.3.0",
|
||||
@@ -5861,7 +5858,7 @@ dependencies = [
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"tempfile",
|
||||
"thiserror 2.0.7",
|
||||
"thiserror 2.0.11",
|
||||
"time",
|
||||
"tokio",
|
||||
"url",
|
||||
@@ -5881,7 +5878,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.7",
|
||||
"thiserror 2.0.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5898,7 +5895,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri-utils",
|
||||
"thiserror 2.0.7",
|
||||
"thiserror 2.0.11",
|
||||
"url",
|
||||
"windows",
|
||||
]
|
||||
@@ -5958,7 +5955,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"swift-rs",
|
||||
"thiserror 2.0.7",
|
||||
"thiserror 2.0.11",
|
||||
"toml 0.8.19",
|
||||
"url",
|
||||
"urlpattern",
|
||||
@@ -6026,11 +6023,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.7"
|
||||
version = "2.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93605438cbd668185516ab499d589afb7ee1859ea3d5fc8f6b0755e1c7443767"
|
||||
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.7",
|
||||
"thiserror-impl 2.0.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6046,9 +6043,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.7"
|
||||
version = "2.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1d8749b4531af2117677a5fcd12b1348a3fe2b81e36e61ffeac5c4aa3273e36"
|
||||
checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -6458,7 +6455,7 @@ dependencies = [
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"sha1",
|
||||
"thiserror 2.0.7",
|
||||
"thiserror 2.0.11",
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
@@ -7406,7 +7403,7 @@ dependencies = [
|
||||
"sha2",
|
||||
"soup3",
|
||||
"tao-macros",
|
||||
"thiserror 2.0.7",
|
||||
"thiserror 2.0.11",
|
||||
"url",
|
||||
"webkit2gtk",
|
||||
"webkit2gtk-sys",
|
||||
@@ -7497,6 +7494,7 @@ dependencies = [
|
||||
"hex_color",
|
||||
"http",
|
||||
"log",
|
||||
"md5",
|
||||
"mime_guess",
|
||||
"objc",
|
||||
"openssl-sys",
|
||||
@@ -7571,7 +7569,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.7",
|
||||
"thiserror 2.0.11",
|
||||
"ts-rs",
|
||||
"yaak-models",
|
||||
]
|
||||
@@ -7592,7 +7590,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"tauri",
|
||||
"thiserror 1.0.63",
|
||||
"thiserror 2.0.11",
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
@@ -7603,6 +7601,7 @@ dependencies = [
|
||||
"dunce",
|
||||
"futures-util",
|
||||
"log",
|
||||
"md5",
|
||||
"path-slash",
|
||||
"rand 0.8.5",
|
||||
"regex",
|
||||
@@ -7610,7 +7609,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-plugin-shell",
|
||||
"thiserror 2.0.7",
|
||||
"thiserror 2.0.11",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"ts-rs",
|
||||
@@ -7639,7 +7638,7 @@ dependencies = [
|
||||
"sha1",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.7",
|
||||
"thiserror 2.0.11",
|
||||
"tokio",
|
||||
"ts-rs",
|
||||
"yaak-models",
|
||||
|
||||
@@ -44,6 +44,7 @@ eventsource-client = { git = "https://github.com/yaakapp/rust-eventsource-client
|
||||
hex_color = "3.0.0"
|
||||
http = { version = "1.2.0", default-features = false }
|
||||
log = "0.4.21"
|
||||
md5 = "0.7.0"
|
||||
rand = "0.8.5"
|
||||
regex = "1.10.2"
|
||||
reqwest = { workspace = true, features = ["multipart", "cookies", "gzip", "brotli", "deflate", "json", "rustls-tls-manual-roots-no-provider"] }
|
||||
@@ -85,7 +86,7 @@ yaak-plugins = { path = "yaak-plugins" }
|
||||
serde = "1.0.215"
|
||||
serde_json = "1.0.132"
|
||||
tauri-plugin-shell = "2.2.0"
|
||||
tauri = "2.2.0"
|
||||
tauri = "2.2.3"
|
||||
thiserror = "2.0.3"
|
||||
ts-rs = "10.0.0"
|
||||
reqwest = "0.12.12"
|
||||
|
||||
@@ -30,10 +30,8 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"opener:allow-open-url",
|
||||
"opener:allow-open-path",
|
||||
"opener:allow-default-urls",
|
||||
"opener:allow-reveal-item-in-dir",
|
||||
"clipboard-manager:allow-read-text",
|
||||
"clipboard-manager:allow-write-text",
|
||||
"core:webview:allow-set-webview-zoom",
|
||||
"core:window:allow-close",
|
||||
"core:window:allow-internal-toggle-maximize",
|
||||
@@ -47,8 +45,11 @@
|
||||
"core:window:allow-start-dragging",
|
||||
"core:window:allow-theme",
|
||||
"core:window:allow-unmaximize",
|
||||
"clipboard-manager:allow-read-text",
|
||||
"clipboard-manager:allow-write-text",
|
||||
"opener:allow-default-urls",
|
||||
"opener:allow-open-path",
|
||||
"opener:allow-open-url",
|
||||
"opener:allow-reveal-item-in-dir",
|
||||
"shell:allow-open",
|
||||
"yaak-license:default",
|
||||
"yaak-sync:default"
|
||||
]
|
||||
|
||||
Generated
+1
-1
@@ -1 +1 @@
|
||||
{"main":{"identifier":"main","description":"Main permissions","local":true,"windows":["*"],"permissions":["core:event:allow-emit","core:event:allow-listen","core:event:allow-unlisten","os:allow-os-type","clipboard-manager:allow-clear","clipboard-manager:allow-write-text","clipboard-manager:allow-read-text","dialog:allow-open","dialog:allow-save","fs:allow-read-dir","fs:allow-read-file","fs:allow-read-text-file",{"identifier":"fs:scope","allow":[{"path":"$APPDATA"},{"path":"$APPDATA/**"}]},"opener:allow-open-url","opener:allow-open-path","opener:allow-default-urls","opener:allow-reveal-item-in-dir","core:webview:allow-set-webview-zoom","core:window:allow-close","core:window:allow-internal-toggle-maximize","core:window:allow-is-fullscreen","core:window:allow-is-maximized","core:window:allow-maximize","core:window:allow-minimize","core:window:allow-set-decorations","core:window:allow-set-title","core:window:allow-show","core:window:allow-start-dragging","core:window:allow-theme","core:window:allow-unmaximize","clipboard-manager:allow-read-text","clipboard-manager:allow-write-text","yaak-license:default","yaak-sync:default"]}}
|
||||
{"main":{"identifier":"main","description":"Main permissions","local":true,"windows":["*"],"permissions":["core:event:allow-emit","core:event:allow-listen","core:event:allow-unlisten","os:allow-os-type","clipboard-manager:allow-clear","clipboard-manager:allow-write-text","clipboard-manager:allow-read-text","dialog:allow-open","dialog:allow-save","fs:allow-read-dir","fs:allow-read-file","fs:allow-read-text-file",{"identifier":"fs:scope","allow":[{"path":"$APPDATA"},{"path":"$APPDATA/**"}]},"clipboard-manager:allow-read-text","clipboard-manager:allow-write-text","core:webview:allow-set-webview-zoom","core:window:allow-close","core:window:allow-internal-toggle-maximize","core:window:allow-is-fullscreen","core:window:allow-is-maximized","core:window:allow-maximize","core:window:allow-minimize","core:window:allow-set-decorations","core:window:allow-set-title","core:window:allow-show","core:window:allow-start-dragging","core:window:allow-theme","core:window:allow-unmaximize","opener:allow-default-urls","opener:allow-open-path","opener:allow-open-url","opener:allow-reveal-item-in-dir","shell:allow-open","yaak-license:default","yaak-sync:default"]}}
|
||||
@@ -0,0 +1,11 @@
|
||||
CREATE TABLE plugin_key_values
|
||||
(
|
||||
model TEXT DEFAULT 'plugin_key_value' NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
deleted_at DATETIME,
|
||||
plugin_name TEXT NOT NULL,
|
||||
key TEXT NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
PRIMARY KEY (plugin_name, key)
|
||||
);
|
||||
@@ -70,7 +70,7 @@ pub async fn send_http_request<R: Runtime>(
|
||||
if !url_string.starts_with("http://") && !url_string.starts_with("https://") {
|
||||
url_string = format!("http://{}", url_string);
|
||||
}
|
||||
debug!("Sending request to {url_string}");
|
||||
debug!("Sending request to {} {url_string}", request.method);
|
||||
|
||||
let mut client_builder = reqwest::Client::builder()
|
||||
.redirect(match workspace.setting_follow_redirects {
|
||||
@@ -262,7 +262,7 @@ pub async fn send_http_request<R: Runtime>(
|
||||
None => {}
|
||||
Some(a) => {
|
||||
for p in a {
|
||||
let enabled = get_bool(p, "enabled");
|
||||
let enabled = get_bool(p, "enabled", true);
|
||||
let name = get_str(p, "name");
|
||||
if !enabled || name.is_empty() {
|
||||
continue;
|
||||
@@ -296,7 +296,7 @@ pub async fn send_http_request<R: Runtime>(
|
||||
None => {}
|
||||
Some(fd) => {
|
||||
for p in fd {
|
||||
let enabled = get_bool(p, "enabled");
|
||||
let enabled = get_bool(p, "enabled", true);
|
||||
let name = get_str(p, "name").to_string();
|
||||
|
||||
if !enabled || name.is_empty() {
|
||||
@@ -376,13 +376,11 @@ pub async fn send_http_request<R: Runtime>(
|
||||
|
||||
if let Some(auth_name) = request.authentication_type.to_owned() {
|
||||
let req = CallHttpAuthenticationRequest {
|
||||
config: serde_json::to_value(&request.authentication)
|
||||
.unwrap()
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.to_owned(),
|
||||
method: sendable_req.method().to_string(),
|
||||
context_id: format!("{:x}", md5::compute(request.id)),
|
||||
values: serde_json::from_value(serde_json::to_value(&request.authentication).unwrap())
|
||||
.unwrap(),
|
||||
url: sendable_req.url().to_string(),
|
||||
method: sendable_req.method().to_string(),
|
||||
headers: sendable_req
|
||||
.headers()
|
||||
.iter()
|
||||
@@ -604,10 +602,10 @@ fn ensure_proto(url_str: &str) -> String {
|
||||
format!("http://{url_str}")
|
||||
}
|
||||
|
||||
fn get_bool(v: &Value, key: &str) -> bool {
|
||||
fn get_bool(v: &Value, key: &str, fallback: bool) -> bool {
|
||||
match v.get(key) {
|
||||
None => false,
|
||||
Some(v) => v.as_bool().unwrap_or_default(),
|
||||
None => fallback,
|
||||
Some(v) => v.as_bool().unwrap_or(fallback),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+84
-335
@@ -6,33 +6,25 @@ use crate::encoding::read_response_body;
|
||||
use crate::grpc::metadata_to_map;
|
||||
use crate::http_request::send_http_request;
|
||||
use crate::notifications::YaakNotifier;
|
||||
use crate::render::{render_grpc_request, render_http_request, render_json_value, render_template};
|
||||
use crate::render::{render_grpc_request, render_template};
|
||||
use crate::template_callback::PluginTemplateCallback;
|
||||
use crate::updates::{UpdateMode, YaakUpdater};
|
||||
use crate::window_menu::app_menu;
|
||||
use chrono::Utc;
|
||||
use eventsource_client::{EventParser, SSE};
|
||||
use log::{debug, error, info, warn};
|
||||
use log::{debug, error, warn};
|
||||
use rand::random;
|
||||
use regex::Regex;
|
||||
use serde::Serialize;
|
||||
use serde_json::{json, Value};
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::fs::{create_dir_all, File};
|
||||
use std::path::PathBuf;
|
||||
use std::process::exit;
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
use std::{fs, panic};
|
||||
#[cfg(target_os = "macos")]
|
||||
use tauri::TitleBarStyle;
|
||||
use tauri::{AppHandle, Emitter, LogicalSize, RunEvent, State, WebviewUrl, WebviewWindow};
|
||||
use tauri::{AppHandle, Emitter, RunEvent, State, WebviewWindow};
|
||||
use tauri::{Listener, Runtime};
|
||||
use tauri::{Manager, WindowEvent};
|
||||
use tauri_plugin_clipboard_manager::ClipboardExt;
|
||||
use tauri_plugin_log::fern::colors::ColoredLevelConfig;
|
||||
use tauri_plugin_log::{Builder, Target, TargetKind};
|
||||
use tauri_plugin_opener::OpenerExt;
|
||||
use tauri_plugin_window_state::{AppHandleExt, StateFlags};
|
||||
use tokio::fs::read_to_string;
|
||||
use tokio::sync::Mutex;
|
||||
@@ -51,28 +43,24 @@ use yaak_models::queries::{
|
||||
delete_all_http_responses_for_workspace, delete_cookie_jar, delete_environment, delete_folder,
|
||||
delete_grpc_connection, delete_grpc_request, delete_http_request, delete_http_response,
|
||||
delete_plugin, delete_workspace, duplicate_folder, duplicate_grpc_request,
|
||||
duplicate_http_request, ensure_base_environment, generate_id, generate_model_id,
|
||||
get_base_environment, get_cookie_jar, get_environment, get_folder, get_grpc_connection,
|
||||
get_grpc_request, get_http_request, get_http_response, get_key_value_raw,
|
||||
get_or_create_settings, get_or_create_workspace_meta, get_plugin, get_workspace,
|
||||
get_workspace_export_resources, list_cookie_jars, list_environments, list_folders,
|
||||
list_grpc_connections_for_workspace, list_grpc_events, list_grpc_requests, list_http_requests,
|
||||
list_http_responses_for_request, list_http_responses_for_workspace, list_key_values_raw,
|
||||
list_plugins, list_workspaces, set_key_value_raw, update_response_if_id, update_settings,
|
||||
upsert_cookie_jar, upsert_environment, upsert_folder, upsert_grpc_connection,
|
||||
duplicate_http_request, ensure_base_environment, generate_model_id, get_base_environment,
|
||||
get_cookie_jar, get_environment, get_folder, get_grpc_connection, get_grpc_request,
|
||||
get_http_request, get_http_response, get_key_value_raw, get_or_create_settings,
|
||||
get_or_create_workspace_meta, get_plugin, get_workspace, get_workspace_export_resources,
|
||||
list_cookie_jars, list_environments, list_folders, list_grpc_connections_for_workspace,
|
||||
list_grpc_events, list_grpc_requests, list_http_requests, list_http_responses_for_workspace,
|
||||
list_key_values_raw, list_plugins, list_workspaces, set_key_value_raw, update_response_if_id,
|
||||
update_settings, upsert_cookie_jar, upsert_environment, upsert_folder, upsert_grpc_connection,
|
||||
upsert_grpc_event, upsert_grpc_request, upsert_http_request, upsert_plugin, upsert_workspace,
|
||||
upsert_workspace_meta, BatchUpsertResult, UpdateSource,
|
||||
};
|
||||
use yaak_plugins::events::{
|
||||
BootResponse, CallHttpAuthenticationRequest, CallHttpRequestActionRequest, Color,
|
||||
FilterResponse, FindHttpResponsesResponse, GetHttpAuthenticationResponse,
|
||||
GetHttpRequestActionsResponse, GetHttpRequestByIdResponse, GetTemplateFunctionsResponse,
|
||||
HttpHeader, Icon, InternalEvent, InternalEventPayload, PromptTextResponse,
|
||||
RenderHttpRequestResponse, RenderPurpose, SendHttpRequestResponse, ShowToastRequest,
|
||||
TemplateRenderResponse, WindowContext,
|
||||
BootResponse, CallHttpAuthenticationRequest, CallHttpRequestActionRequest, FilterResponse,
|
||||
GetHttpAuthenticationConfigResponse, GetHttpAuthenticationSummaryResponse,
|
||||
GetHttpRequestActionsResponse, GetTemplateFunctionsResponse, HttpHeader, InternalEvent,
|
||||
InternalEventPayload, JsonPrimitive, RenderPurpose, WindowContext,
|
||||
};
|
||||
use yaak_plugins::manager::PluginManager;
|
||||
use yaak_plugins::plugin_handle::PluginHandle;
|
||||
use yaak_sse::sse::ServerSentEvent;
|
||||
use yaak_templates::format::format_json;
|
||||
use yaak_templates::{Parser, Tokens};
|
||||
@@ -82,11 +70,13 @@ mod encoding;
|
||||
mod grpc;
|
||||
mod http_request;
|
||||
mod notifications;
|
||||
mod plugin_events;
|
||||
mod render;
|
||||
#[cfg(target_os = "macos")]
|
||||
mod tauri_plugin_mac_window;
|
||||
mod template_callback;
|
||||
mod updates;
|
||||
mod window;
|
||||
mod window_menu;
|
||||
|
||||
const DEFAULT_WINDOW_WIDTH: f64 = 1100.0;
|
||||
@@ -242,7 +232,8 @@ async fn cmd_grpc_go<R: Runtime>(
|
||||
if let Some(auth_name) = request.authentication_type.clone() {
|
||||
let auth = request.authentication.clone();
|
||||
let plugin_req = CallHttpAuthenticationRequest {
|
||||
config: serde_json::to_value(&auth).unwrap().as_object().unwrap().to_owned(),
|
||||
context_id: format!("{:x}", md5::compute(request_id.to_string())),
|
||||
values: serde_json::from_value(serde_json::to_value(&auth).unwrap()).unwrap(),
|
||||
method: "POST".to_string(),
|
||||
url: request.url.clone(),
|
||||
headers: metadata
|
||||
@@ -969,15 +960,31 @@ async fn cmd_template_functions<R: Runtime>(
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cmd_get_http_authentication<R: Runtime>(
|
||||
async fn cmd_get_http_authentication_summaries<R: Runtime>(
|
||||
window: WebviewWindow<R>,
|
||||
plugin_manager: State<'_, PluginManager>,
|
||||
) -> Result<Vec<GetHttpAuthenticationResponse>, String> {
|
||||
let results =
|
||||
plugin_manager.get_http_authentication(&window).await.map_err(|e| e.to_string())?;
|
||||
) -> Result<Vec<GetHttpAuthenticationSummaryResponse>, String> {
|
||||
let results = plugin_manager
|
||||
.get_http_authentication_summaries(&window)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
Ok(results.into_iter().map(|(_, a)| a).collect())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cmd_get_http_authentication_config<R: Runtime>(
|
||||
window: WebviewWindow<R>,
|
||||
plugin_manager: State<'_, PluginManager>,
|
||||
auth_name: &str,
|
||||
values: HashMap<String, JsonPrimitive>,
|
||||
request_id: &str,
|
||||
) -> Result<GetHttpAuthenticationConfigResponse, String> {
|
||||
plugin_manager
|
||||
.get_http_authentication_config(&window, auth_name, values, request_id)
|
||||
.await
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cmd_call_http_request_action<R: Runtime>(
|
||||
window: WebviewWindow<R>,
|
||||
@@ -987,6 +994,21 @@ async fn cmd_call_http_request_action<R: Runtime>(
|
||||
plugin_manager.call_http_request_action(&window, req).await.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cmd_call_http_authentication_action<R: Runtime>(
|
||||
window: WebviewWindow<R>,
|
||||
plugin_manager: State<'_, PluginManager>,
|
||||
auth_name: &str,
|
||||
action_index: i32,
|
||||
values: HashMap<String, JsonPrimitive>,
|
||||
request_id: &str,
|
||||
) -> Result<(), String> {
|
||||
plugin_manager
|
||||
.call_http_authentication_action(&window, auth_name, action_index, values, request_id)
|
||||
.await
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cmd_curl_to_request<R: Runtime>(
|
||||
window: WebviewWindow<R>,
|
||||
@@ -1175,7 +1197,7 @@ async fn cmd_install_plugin<R: Runtime>(
|
||||
window: WebviewWindow<R>,
|
||||
) -> Result<Plugin, String> {
|
||||
plugin_manager
|
||||
.add_plugin_by_dir(WindowContext::from_window(&window), &directory, true)
|
||||
.add_plugin_by_dir(&WindowContext::from_window(&window), &directory, true)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
@@ -1205,7 +1227,7 @@ async fn cmd_uninstall_plugin<R: Runtime>(
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
plugin_manager
|
||||
.uninstall(WindowContext::from_window(&window), plugin.directory.as_str())
|
||||
.uninstall(&WindowContext::from_window(&window), plugin.directory.as_str())
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
@@ -1458,7 +1480,7 @@ async fn cmd_reload_plugins<R: Runtime>(
|
||||
plugin_manager: State<'_, PluginManager>,
|
||||
) -> Result<(), String> {
|
||||
plugin_manager
|
||||
.initialize_all_plugins(window.app_handle(), WindowContext::from_window(&window))
|
||||
.initialize_all_plugins(window.app_handle(), &WindowContext::from_window(&window))
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
@@ -1656,15 +1678,17 @@ async fn cmd_new_child_window(
|
||||
current_pos.y + current_size.height / 2.0 - inner_size.1 / 2.0,
|
||||
);
|
||||
|
||||
let config = CreateWindowConfig {
|
||||
let config = window::CreateWindowConfig {
|
||||
label: label.as_str(),
|
||||
title,
|
||||
url,
|
||||
inner_size,
|
||||
position,
|
||||
inner_size: Some(inner_size),
|
||||
position: Some(position),
|
||||
navigation_tx: None,
|
||||
hide_titlebar: true,
|
||||
};
|
||||
|
||||
let child_window = create_window(&app_handle, config);
|
||||
let child_window = window::create_window(&app_handle, config);
|
||||
|
||||
// NOTE: These listeners will remain active even when the windows close. Unfortunately,
|
||||
// there's no way to unlisten to events for now, so we just have to be defensive.
|
||||
@@ -1830,6 +1854,7 @@ pub fn run() {
|
||||
Ok(())
|
||||
})
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
cmd_call_http_authentication_action,
|
||||
cmd_call_http_request_action,
|
||||
cmd_check_for_updates,
|
||||
cmd_create_cookie_jar,
|
||||
@@ -1859,7 +1884,8 @@ pub fn run() {
|
||||
cmd_get_environment,
|
||||
cmd_get_folder,
|
||||
cmd_get_grpc_request,
|
||||
cmd_get_http_authentication,
|
||||
cmd_get_http_authentication_summaries,
|
||||
cmd_get_http_authentication_config,
|
||||
cmd_get_http_request,
|
||||
cmd_get_key_value,
|
||||
cmd_get_settings,
|
||||
@@ -1998,113 +2024,21 @@ fn create_main_window(handle: &AppHandle, url: &str) -> WebviewWindow {
|
||||
}
|
||||
.expect("Failed to generate label for new window");
|
||||
|
||||
let config = CreateWindowConfig {
|
||||
let config = window::CreateWindowConfig {
|
||||
url,
|
||||
label: label.as_str(),
|
||||
title: "Yaak",
|
||||
inner_size: (DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT),
|
||||
position: (
|
||||
inner_size: Some((DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT)),
|
||||
position: Some((
|
||||
// Offset by random amount so it's easier to differentiate
|
||||
100.0 + random::<f64>() * 20.0,
|
||||
100.0 + random::<f64>() * 20.0,
|
||||
),
|
||||
)),
|
||||
navigation_tx: None,
|
||||
hide_titlebar: true,
|
||||
};
|
||||
|
||||
create_window(handle, config)
|
||||
}
|
||||
|
||||
struct CreateWindowConfig<'s> {
|
||||
url: &'s str,
|
||||
label: &'s str,
|
||||
title: &'s str,
|
||||
inner_size: (f64, f64),
|
||||
position: (f64, f64),
|
||||
}
|
||||
|
||||
fn create_window(handle: &AppHandle, config: CreateWindowConfig) -> WebviewWindow {
|
||||
#[allow(unused_variables)]
|
||||
let menu = app_menu(handle).unwrap();
|
||||
|
||||
// This causes the window to not be clickable (in AppImage), so disable on Linux
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
handle.set_menu(menu).expect("Failed to set app menu");
|
||||
|
||||
info!("Create new window label={}", config.label);
|
||||
|
||||
let mut win_builder =
|
||||
tauri::WebviewWindowBuilder::new(handle, config.label, WebviewUrl::App(config.url.into()))
|
||||
.title(config.title)
|
||||
.resizable(true)
|
||||
.visible(false) // To prevent theme flashing, the frontend code calls show() immediately after configuring the theme
|
||||
.fullscreen(false)
|
||||
.disable_drag_drop_handler() // Required for frontend Dnd on windows
|
||||
.inner_size(config.inner_size.0, config.inner_size.1)
|
||||
.position(config.position.0, config.position.1)
|
||||
.min_inner_size(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT);
|
||||
|
||||
// Add macOS-only things
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
win_builder = win_builder.hidden_title(true).title_bar_style(TitleBarStyle::Overlay);
|
||||
}
|
||||
|
||||
// Add non-MacOS things
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
{
|
||||
// Doesn't seem to work from Rust, here, so we do it in main.tsx
|
||||
win_builder = win_builder.decorations(false);
|
||||
}
|
||||
|
||||
if let Some(w) = handle.webview_windows().get(config.label) {
|
||||
info!("Webview with label {} already exists. Focusing existing", config.label);
|
||||
w.set_focus().unwrap();
|
||||
return w.to_owned();
|
||||
}
|
||||
|
||||
let win = win_builder.build().unwrap();
|
||||
|
||||
let webview_window = win.clone();
|
||||
win.on_menu_event(move |w, event| {
|
||||
if !w.is_focused().unwrap() {
|
||||
return;
|
||||
}
|
||||
|
||||
let event_id = event.id().0.as_str();
|
||||
match event_id {
|
||||
"quit" => exit(0),
|
||||
"close" => w.close().unwrap(),
|
||||
"zoom_reset" => w.emit("zoom_reset", true).unwrap(),
|
||||
"zoom_in" => w.emit("zoom_in", true).unwrap(),
|
||||
"zoom_out" => w.emit("zoom_out", true).unwrap(),
|
||||
"settings" => w.emit("settings", true).unwrap(),
|
||||
"open_feedback" => {
|
||||
if let Err(e) =
|
||||
w.app_handle().opener().open_url("https://yaak.app/feedback", None::<&str>)
|
||||
{
|
||||
warn!("Failed to open feedback {e:?}")
|
||||
}
|
||||
}
|
||||
|
||||
// Commands for development
|
||||
"dev.reset_size" => webview_window
|
||||
.set_size(LogicalSize::new(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT))
|
||||
.unwrap(),
|
||||
"dev.refresh" => webview_window.eval("location.reload()").unwrap(),
|
||||
"dev.generate_theme_css" => {
|
||||
w.emit("generate_theme_css", true).unwrap();
|
||||
}
|
||||
"dev.toggle_devtools" => {
|
||||
if webview_window.is_devtools_open() {
|
||||
webview_window.close_devtools();
|
||||
} else {
|
||||
webview_window.open_devtools();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
|
||||
win
|
||||
window::create_window(handle, config)
|
||||
}
|
||||
|
||||
async fn get_update_mode(h: &AppHandle) -> UpdateMode {
|
||||
@@ -2140,36 +2074,24 @@ fn monitor_plugin_events<R: Runtime>(app_handle: &AppHandle<R>) {
|
||||
// We might have recursive back-and-forth calls between app and plugin, so we don't
|
||||
// want to block here
|
||||
tauri::async_runtime::spawn(async move {
|
||||
handle_plugin_event(&app_handle, &event, &plugin).await;
|
||||
crate::plugin_events::handle_plugin_event(&app_handle, &event, &plugin).await;
|
||||
});
|
||||
}
|
||||
plugin_manager.unsubscribe(rx_id.as_str()).await;
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct FrontendCall<T: Serialize + Clone> {
|
||||
args: T,
|
||||
reply_id: String,
|
||||
}
|
||||
|
||||
async fn call_frontend<T: Serialize + Clone, R: Runtime>(
|
||||
async fn call_frontend<R: Runtime>(
|
||||
window: WebviewWindow<R>,
|
||||
event_name: &str,
|
||||
args: T,
|
||||
) -> PromptTextResponse {
|
||||
let reply_id = format!("{event_name}_reply_{}", generate_id());
|
||||
let payload = FrontendCall {
|
||||
args,
|
||||
reply_id: reply_id.clone(),
|
||||
};
|
||||
window.emit_to(window.label(), event_name, payload).unwrap();
|
||||
let (tx, mut rx) = tokio::sync::watch::channel(PromptTextResponse::default());
|
||||
event: &InternalEvent,
|
||||
) -> Option<InternalEventPayload> {
|
||||
window.emit_to(window.label(), "plugin_event", event.clone()).unwrap();
|
||||
let (tx, mut rx) = tokio::sync::watch::channel(None);
|
||||
|
||||
let reply_id = event.id.clone();
|
||||
let event_id = window.clone().listen(reply_id, move |ev| {
|
||||
let resp: PromptTextResponse = serde_json::from_str(ev.payload()).unwrap();
|
||||
if let Err(e) = tx.send(resp) {
|
||||
let resp: InternalEvent = serde_json::from_str(ev.payload()).unwrap();
|
||||
if let Err(e) = tx.send(Some(resp.payload)) {
|
||||
warn!("Failed to prompt for text {e:?}");
|
||||
}
|
||||
});
|
||||
@@ -2181,180 +2103,7 @@ async fn call_frontend<T: Serialize + Clone, R: Runtime>(
|
||||
window.unlisten(event_id);
|
||||
|
||||
let v = rx.borrow();
|
||||
v.clone()
|
||||
}
|
||||
|
||||
async fn handle_plugin_event<R: Runtime>(
|
||||
app_handle: &AppHandle<R>,
|
||||
event: &InternalEvent,
|
||||
plugin_handle: &PluginHandle,
|
||||
) {
|
||||
// info!("Got event to app {}", event.id);
|
||||
let window_context = event.window_context.to_owned();
|
||||
let response_event: Option<InternalEventPayload> = match event.clone().payload {
|
||||
InternalEventPayload::CopyTextRequest(req) => {
|
||||
app_handle
|
||||
.clipboard()
|
||||
.write_text(req.text.as_str())
|
||||
.expect("Failed to write text to clipboard");
|
||||
None
|
||||
}
|
||||
InternalEventPayload::ShowToastRequest(req) => {
|
||||
match window_context {
|
||||
WindowContext::Label { label } => app_handle
|
||||
.emit_to(label, "show_toast", req)
|
||||
.expect("Failed to emit show_toast to window"),
|
||||
_ => app_handle.emit("show_toast", req).expect("Failed to emit show_toast"),
|
||||
};
|
||||
None
|
||||
}
|
||||
InternalEventPayload::PromptTextRequest(req) => {
|
||||
let window = get_window_from_window_context(app_handle, &window_context)
|
||||
.expect("Failed to find window for render");
|
||||
let resp = call_frontend(window, "show_prompt", req).await;
|
||||
Some(InternalEventPayload::PromptTextResponse(resp))
|
||||
}
|
||||
InternalEventPayload::FindHttpResponsesRequest(req) => {
|
||||
let http_responses = list_http_responses_for_request(
|
||||
app_handle,
|
||||
req.request_id.as_str(),
|
||||
req.limit.map(|l| l as i64),
|
||||
)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
Some(InternalEventPayload::FindHttpResponsesResponse(FindHttpResponsesResponse {
|
||||
http_responses,
|
||||
}))
|
||||
}
|
||||
InternalEventPayload::GetHttpRequestByIdRequest(req) => {
|
||||
let http_request = get_http_request(app_handle, req.id.as_str()).await.unwrap();
|
||||
Some(InternalEventPayload::GetHttpRequestByIdResponse(GetHttpRequestByIdResponse {
|
||||
http_request,
|
||||
}))
|
||||
}
|
||||
InternalEventPayload::RenderHttpRequestRequest(req) => {
|
||||
let window = get_window_from_window_context(app_handle, &window_context)
|
||||
.expect("Failed to find window for render http request");
|
||||
|
||||
let workspace = workspace_from_window(&window)
|
||||
.await
|
||||
.expect("Failed to get workspace_id from window URL");
|
||||
let environment = environment_from_window(&window).await;
|
||||
let base_environment = get_base_environment(&window, workspace.id.as_str())
|
||||
.await
|
||||
.expect("Failed to get base environment");
|
||||
let cb = PluginTemplateCallback::new(app_handle, &window_context, req.purpose);
|
||||
let http_request = render_http_request(
|
||||
&req.http_request,
|
||||
&base_environment,
|
||||
environment.as_ref(),
|
||||
&cb,
|
||||
)
|
||||
.await;
|
||||
Some(InternalEventPayload::RenderHttpRequestResponse(RenderHttpRequestResponse {
|
||||
http_request,
|
||||
}))
|
||||
}
|
||||
InternalEventPayload::TemplateRenderRequest(req) => {
|
||||
let window = get_window_from_window_context(app_handle, &window_context)
|
||||
.expect("Failed to find window for render");
|
||||
|
||||
let workspace = workspace_from_window(&window)
|
||||
.await
|
||||
.expect("Failed to get workspace_id from window URL");
|
||||
let environment = environment_from_window(&window).await;
|
||||
let base_environment = get_base_environment(&window, workspace.id.as_str())
|
||||
.await
|
||||
.expect("Failed to get base environment");
|
||||
let cb = PluginTemplateCallback::new(app_handle, &window_context, req.purpose);
|
||||
let data =
|
||||
render_json_value(req.data, &base_environment, environment.as_ref(), &cb).await;
|
||||
Some(InternalEventPayload::TemplateRenderResponse(TemplateRenderResponse { data }))
|
||||
}
|
||||
InternalEventPayload::ErrorResponse(resp) => {
|
||||
let window = get_window_from_window_context(app_handle, &window_context)
|
||||
.expect("Failed to find window for plugin reload");
|
||||
let toast_event = plugin_handle.build_event_to_send(
|
||||
WindowContext::from_window(&window),
|
||||
&InternalEventPayload::ShowToastRequest(ShowToastRequest {
|
||||
message: resp.error,
|
||||
color: Some(Color::Danger),
|
||||
..Default::default()
|
||||
}),
|
||||
None,
|
||||
);
|
||||
Box::pin(handle_plugin_event(app_handle, &toast_event, plugin_handle)).await;
|
||||
None
|
||||
}
|
||||
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();
|
||||
for plugin in plugins {
|
||||
if plugin.directory != plugin_handle.dir {
|
||||
continue;
|
||||
}
|
||||
|
||||
let new_plugin = Plugin {
|
||||
updated_at: Utc::now().naive_utc(), // TODO: Add reloaded_at field to use instead
|
||||
..plugin
|
||||
};
|
||||
upsert_plugin(&window, new_plugin, &UpdateSource::Plugin).await.unwrap();
|
||||
}
|
||||
let toast_event = plugin_handle.build_event_to_send(
|
||||
WindowContext::from_window(&window),
|
||||
&InternalEventPayload::ShowToastRequest(ShowToastRequest {
|
||||
message: format!("Reloaded plugin {}", plugin_handle.dir),
|
||||
icon: Some(Icon::Info),
|
||||
..Default::default()
|
||||
}),
|
||||
None,
|
||||
);
|
||||
Box::pin(handle_plugin_event(app_handle, &toast_event, plugin_handle)).await;
|
||||
None
|
||||
}
|
||||
InternalEventPayload::SendHttpRequestRequest(req) => {
|
||||
let window = get_window_from_window_context(app_handle, &window_context)
|
||||
.expect("Failed to find window for sending HTTP request");
|
||||
let cookie_jar = cookie_jar_from_window(&window).await;
|
||||
let environment = environment_from_window(&window).await;
|
||||
|
||||
let resp = create_default_http_response(
|
||||
&window,
|
||||
req.http_request.id.as_str(),
|
||||
&UpdateSource::Plugin,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let result = send_http_request(
|
||||
&window,
|
||||
&req.http_request,
|
||||
&resp,
|
||||
environment,
|
||||
cookie_jar,
|
||||
&mut tokio::sync::watch::channel(false).1, // No-op cancel channel
|
||||
)
|
||||
.await;
|
||||
|
||||
let http_response = match result {
|
||||
Ok(r) => r,
|
||||
Err(_e) => return,
|
||||
};
|
||||
|
||||
Some(InternalEventPayload::SendHttpRequestResponse(SendHttpRequestResponse {
|
||||
http_response,
|
||||
}))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(e) = response_event {
|
||||
let plugin_manager: State<'_, PluginManager> = app_handle.state();
|
||||
if let Err(e) = plugin_manager.reply(&event, &e).await {
|
||||
warn!("Failed to reply to plugin manager: {:?}", e)
|
||||
}
|
||||
}
|
||||
v.to_owned()
|
||||
}
|
||||
|
||||
fn get_window_from_window_context<R: Runtime>(
|
||||
|
||||
@@ -0,0 +1,265 @@
|
||||
use crate::http_request::send_http_request;
|
||||
use crate::render::{render_http_request, render_json_value};
|
||||
use crate::template_callback::PluginTemplateCallback;
|
||||
use crate::window::{create_window, CreateWindowConfig};
|
||||
use crate::{
|
||||
call_frontend, cookie_jar_from_window, environment_from_window, get_window_from_window_context,
|
||||
workspace_from_window,
|
||||
};
|
||||
use chrono::Utc;
|
||||
use log::warn;
|
||||
use tauri::{AppHandle, Emitter, Manager, Runtime, State};
|
||||
use tauri_plugin_clipboard_manager::ClipboardExt;
|
||||
use yaak_models::models::{HttpResponse, Plugin};
|
||||
use yaak_models::queries::{
|
||||
create_default_http_response, delete_plugin_key_value, get_base_environment, get_http_request,
|
||||
get_plugin_key_value, list_http_responses_for_request, list_plugins, set_plugin_key_value,
|
||||
upsert_plugin, UpdateSource,
|
||||
};
|
||||
use yaak_plugins::events::{
|
||||
Color, DeleteKeyValueResponse, EmptyPayload, FindHttpResponsesResponse,
|
||||
GetHttpRequestByIdResponse, GetKeyValueResponse, Icon, InternalEvent, InternalEventPayload,
|
||||
RenderHttpRequestResponse, SendHttpRequestResponse, SetKeyValueResponse, ShowToastRequest,
|
||||
TemplateRenderResponse, WindowContext, WindowNavigateEvent,
|
||||
};
|
||||
use yaak_plugins::manager::PluginManager;
|
||||
use yaak_plugins::plugin_handle::PluginHandle;
|
||||
|
||||
pub(crate) async fn handle_plugin_event<R: Runtime>(
|
||||
app_handle: &AppHandle<R>,
|
||||
event: &InternalEvent,
|
||||
plugin_handle: &PluginHandle,
|
||||
) {
|
||||
// info!("Got event to app {}", event.id);
|
||||
let window_context = event.window_context.to_owned();
|
||||
let response_event: Option<InternalEventPayload> = match event.clone().payload {
|
||||
InternalEventPayload::CopyTextRequest(req) => {
|
||||
app_handle
|
||||
.clipboard()
|
||||
.write_text(req.text.as_str())
|
||||
.expect("Failed to write text to clipboard");
|
||||
Some(InternalEventPayload::CopyTextResponse(EmptyPayload {}))
|
||||
}
|
||||
InternalEventPayload::ShowToastRequest(req) => {
|
||||
match window_context {
|
||||
WindowContext::Label { label } => app_handle
|
||||
.emit_to(label, "show_toast", req)
|
||||
.expect("Failed to emit show_toast to window"),
|
||||
_ => app_handle.emit("show_toast", req).expect("Failed to emit show_toast"),
|
||||
};
|
||||
Some(InternalEventPayload::ShowToastResponse(EmptyPayload {}))
|
||||
}
|
||||
InternalEventPayload::PromptTextRequest(_) => {
|
||||
let window = get_window_from_window_context(app_handle, &window_context)
|
||||
.expect("Failed to find window for render");
|
||||
call_frontend(window, event).await
|
||||
}
|
||||
InternalEventPayload::FindHttpResponsesRequest(req) => {
|
||||
let http_responses = list_http_responses_for_request(
|
||||
app_handle,
|
||||
req.request_id.as_str(),
|
||||
req.limit.map(|l| l as i64),
|
||||
)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
Some(InternalEventPayload::FindHttpResponsesResponse(FindHttpResponsesResponse {
|
||||
http_responses,
|
||||
}))
|
||||
}
|
||||
InternalEventPayload::GetHttpRequestByIdRequest(req) => {
|
||||
let http_request = get_http_request(app_handle, req.id.as_str()).await.unwrap();
|
||||
Some(InternalEventPayload::GetHttpRequestByIdResponse(GetHttpRequestByIdResponse {
|
||||
http_request,
|
||||
}))
|
||||
}
|
||||
InternalEventPayload::RenderHttpRequestRequest(req) => {
|
||||
let window = get_window_from_window_context(app_handle, &window_context)
|
||||
.expect("Failed to find window for render http request");
|
||||
|
||||
let workspace = workspace_from_window(&window)
|
||||
.await
|
||||
.expect("Failed to get workspace_id from window URL");
|
||||
let environment = environment_from_window(&window).await;
|
||||
let base_environment = get_base_environment(&window, workspace.id.as_str())
|
||||
.await
|
||||
.expect("Failed to get base environment");
|
||||
let cb = PluginTemplateCallback::new(app_handle, &window_context, req.purpose);
|
||||
let http_request = render_http_request(
|
||||
&req.http_request,
|
||||
&base_environment,
|
||||
environment.as_ref(),
|
||||
&cb,
|
||||
)
|
||||
.await;
|
||||
Some(InternalEventPayload::RenderHttpRequestResponse(RenderHttpRequestResponse {
|
||||
http_request,
|
||||
}))
|
||||
}
|
||||
InternalEventPayload::TemplateRenderRequest(req) => {
|
||||
let window = get_window_from_window_context(app_handle, &window_context)
|
||||
.expect("Failed to find window for render");
|
||||
|
||||
let workspace = workspace_from_window(&window)
|
||||
.await
|
||||
.expect("Failed to get workspace_id from window URL");
|
||||
let environment = environment_from_window(&window).await;
|
||||
let base_environment = get_base_environment(&window, workspace.id.as_str())
|
||||
.await
|
||||
.expect("Failed to get base environment");
|
||||
let cb = PluginTemplateCallback::new(app_handle, &window_context, req.purpose);
|
||||
let data =
|
||||
render_json_value(req.data, &base_environment, environment.as_ref(), &cb).await;
|
||||
Some(InternalEventPayload::TemplateRenderResponse(TemplateRenderResponse { data }))
|
||||
}
|
||||
InternalEventPayload::ErrorResponse(resp) => {
|
||||
let window = get_window_from_window_context(app_handle, &window_context)
|
||||
.expect("Failed to find window for plugin reload");
|
||||
let toast_event = plugin_handle.build_event_to_send(
|
||||
&WindowContext::from_window(&window),
|
||||
&InternalEventPayload::ShowToastRequest(ShowToastRequest {
|
||||
message: format!(
|
||||
"Plugin error from {}: {}",
|
||||
plugin_handle.name().await,
|
||||
resp.error
|
||||
),
|
||||
color: Some(Color::Danger),
|
||||
..Default::default()
|
||||
}),
|
||||
None,
|
||||
);
|
||||
Box::pin(handle_plugin_event(app_handle, &toast_event, plugin_handle)).await;
|
||||
None
|
||||
}
|
||||
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();
|
||||
for plugin in plugins {
|
||||
if plugin.directory != plugin_handle.dir {
|
||||
continue;
|
||||
}
|
||||
|
||||
let new_plugin = Plugin {
|
||||
updated_at: Utc::now().naive_utc(), // TODO: Add reloaded_at field to use instead
|
||||
..plugin
|
||||
};
|
||||
upsert_plugin(&window, new_plugin, &UpdateSource::Plugin).await.unwrap();
|
||||
}
|
||||
let toast_event = plugin_handle.build_event_to_send(
|
||||
&WindowContext::from_window(&window),
|
||||
&InternalEventPayload::ShowToastRequest(ShowToastRequest {
|
||||
message: format!("Reloaded plugin {}", plugin_handle.dir),
|
||||
icon: Some(Icon::Info),
|
||||
..Default::default()
|
||||
}),
|
||||
None,
|
||||
);
|
||||
Box::pin(handle_plugin_event(app_handle, &toast_event, plugin_handle)).await;
|
||||
None
|
||||
}
|
||||
InternalEventPayload::SendHttpRequestRequest(req) => {
|
||||
let window = get_window_from_window_context(app_handle, &window_context)
|
||||
.expect("Failed to find window for sending HTTP request");
|
||||
let mut http_request = req.http_request;
|
||||
let workspace = workspace_from_window(&window)
|
||||
.await
|
||||
.expect("Failed to get workspace_id from window URL");
|
||||
let cookie_jar = cookie_jar_from_window(&window).await;
|
||||
let environment = environment_from_window(&window).await;
|
||||
|
||||
if http_request.workspace_id.is_empty() {
|
||||
http_request.workspace_id = workspace.id;
|
||||
}
|
||||
|
||||
let resp = if http_request.id.is_empty() {
|
||||
HttpResponse::new()
|
||||
} else {
|
||||
create_default_http_response(
|
||||
&window,
|
||||
http_request.id.as_str(),
|
||||
&UpdateSource::Plugin,
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let result = send_http_request(
|
||||
&window,
|
||||
&http_request,
|
||||
&resp,
|
||||
environment,
|
||||
cookie_jar,
|
||||
&mut tokio::sync::watch::channel(false).1, // No-op cancel channel
|
||||
)
|
||||
.await;
|
||||
|
||||
let http_response = match result {
|
||||
Ok(r) => r,
|
||||
Err(_e) => return,
|
||||
};
|
||||
|
||||
Some(InternalEventPayload::SendHttpRequestResponse(SendHttpRequestResponse {
|
||||
http_response,
|
||||
}))
|
||||
}
|
||||
InternalEventPayload::OpenWindowRequest(req) => {
|
||||
let label = req.label;
|
||||
let (tx, mut rx) = tokio::sync::mpsc::channel(128);
|
||||
let win_config = CreateWindowConfig {
|
||||
url: &req.url,
|
||||
label: &label.clone(),
|
||||
title: &req.title.unwrap_or_default(),
|
||||
navigation_tx: Some(tx),
|
||||
inner_size: req.size.map(|s| (s.width, s.height)),
|
||||
position: None,
|
||||
hide_titlebar: false,
|
||||
};
|
||||
create_window(app_handle, win_config);
|
||||
|
||||
let event_id = event.id.clone();
|
||||
let plugin_handle = plugin_handle.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
while let Some(url) = rx.recv().await {
|
||||
let label = label.clone();
|
||||
let url = url.to_string();
|
||||
let event_to_send = plugin_handle.build_event_to_send(
|
||||
&WindowContext::Label { label },
|
||||
&InternalEventPayload::WindowNavigateEvent(WindowNavigateEvent { url }),
|
||||
Some(event_id.clone()),
|
||||
);
|
||||
plugin_handle.send(&event_to_send).await.unwrap();
|
||||
}
|
||||
});
|
||||
None
|
||||
}
|
||||
InternalEventPayload::CloseWindowRequest(req) => {
|
||||
if let Some(window) = app_handle.webview_windows().get(&req.label) {
|
||||
window.close().expect("Failed to close window");
|
||||
}
|
||||
None
|
||||
}
|
||||
InternalEventPayload::SetKeyValueRequest(req) => {
|
||||
let name = plugin_handle.name().await;
|
||||
set_plugin_key_value(app_handle, &name, &req.key, &req.value).await;
|
||||
Some(InternalEventPayload::SetKeyValueResponse(SetKeyValueResponse {}))
|
||||
}
|
||||
InternalEventPayload::GetKeyValueRequest(req) => {
|
||||
let name = plugin_handle.name().await;
|
||||
let value = get_plugin_key_value(app_handle, &name, &req.key).await.map(|v| v.value);
|
||||
Some(InternalEventPayload::GetKeyValueResponse(GetKeyValueResponse { value }))
|
||||
}
|
||||
InternalEventPayload::DeleteKeyValueRequest(req) => {
|
||||
let name = plugin_handle.name().await;
|
||||
let deleted = delete_plugin_key_value(app_handle, &name, &req.key).await;
|
||||
Some(InternalEventPayload::DeleteKeyValueResponse(DeleteKeyValueResponse { deleted }))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(e) = response_event {
|
||||
let plugin_manager: State<'_, PluginManager> = app_handle.state();
|
||||
if let Err(e) = plugin_manager.reply(&event, &e).await {
|
||||
warn!("Failed to reply to plugin manager: {:?}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,14 +28,13 @@ impl PluginTemplateCallback {
|
||||
|
||||
impl TemplateCallback for PluginTemplateCallback {
|
||||
async fn run(&self, fn_name: &str, args: HashMap<String, String>) -> Result<String, String> {
|
||||
let window_context = self.window_context.to_owned();
|
||||
// The beta named the function `Response` but was changed in stable.
|
||||
// Keep this here for a while because there's no easy way to migrate
|
||||
let fn_name = if fn_name == "Response" { "response" } else { fn_name };
|
||||
|
||||
let function = self
|
||||
.plugin_manager
|
||||
.get_template_functions_with_context(window_context.to_owned())
|
||||
.get_template_functions_with_context(&self.window_context)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?
|
||||
.iter()
|
||||
@@ -54,6 +53,9 @@ impl TemplateCallback for PluginTemplateCallback {
|
||||
FormInput::Checkbox(a) => a.base,
|
||||
FormInput::File(a) => a.base,
|
||||
FormInput::HttpRequest(a) => a.base,
|
||||
FormInput::Accordion(_) => continue,
|
||||
FormInput::Banner(_) => continue,
|
||||
FormInput::Markdown(_) => continue,
|
||||
};
|
||||
if let None = args_with_defaults.get(base.name.as_str()) {
|
||||
args_with_defaults.insert(base.name, base.default_value.unwrap_or_default());
|
||||
@@ -63,7 +65,7 @@ impl TemplateCallback for PluginTemplateCallback {
|
||||
let resp = self
|
||||
.plugin_manager
|
||||
.call_template_function(
|
||||
window_context,
|
||||
&self.window_context,
|
||||
fn_name,
|
||||
args_with_defaults,
|
||||
self.render_purpose.to_owned(),
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
use crate::window_menu::app_menu;
|
||||
use crate::{DEFAULT_WINDOW_HEIGHT, DEFAULT_WINDOW_WIDTH, MIN_WINDOW_HEIGHT, MIN_WINDOW_WIDTH};
|
||||
use log::{info, warn};
|
||||
use std::process::exit;
|
||||
use tauri::{
|
||||
AppHandle, Emitter, LogicalSize, Manager, Runtime, TitleBarStyle, WebviewUrl, WebviewWindow,
|
||||
};
|
||||
use tauri_plugin_opener::OpenerExt;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub(crate) struct CreateWindowConfig<'s> {
|
||||
pub url: &'s str,
|
||||
pub label: &'s str,
|
||||
pub title: &'s str,
|
||||
pub inner_size: Option<(f64, f64)>,
|
||||
pub position: Option<(f64, f64)>,
|
||||
pub navigation_tx: Option<mpsc::Sender<String>>,
|
||||
pub hide_titlebar: bool,
|
||||
}
|
||||
|
||||
pub(crate) fn create_window<R: Runtime>(
|
||||
handle: &AppHandle<R>,
|
||||
config: CreateWindowConfig,
|
||||
) -> WebviewWindow<R> {
|
||||
#[allow(unused_variables)]
|
||||
let menu = app_menu(handle).unwrap();
|
||||
|
||||
// This causes the window to not be clickable (in AppImage), so disable on Linux
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
handle.set_menu(menu).expect("Failed to set app menu");
|
||||
|
||||
info!("Create new window label={}", config.label);
|
||||
|
||||
let mut win_builder =
|
||||
tauri::WebviewWindowBuilder::new(handle, config.label, WebviewUrl::App(config.url.into()))
|
||||
.title(config.title)
|
||||
.resizable(true)
|
||||
.visible(false) // To prevent theme flashing, the frontend code calls show() immediately after configuring the theme
|
||||
.fullscreen(false)
|
||||
.disable_drag_drop_handler() // Required for frontend Dnd on windows
|
||||
.min_inner_size(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT);
|
||||
|
||||
if let Some((w, h)) = config.inner_size {
|
||||
win_builder = win_builder.inner_size(w, h);
|
||||
} else {
|
||||
win_builder = win_builder.inner_size(600.0, 600.0);
|
||||
}
|
||||
|
||||
if let Some((x, y)) = config.position {
|
||||
win_builder = win_builder.position(x, y);
|
||||
} else {
|
||||
win_builder = win_builder.center();
|
||||
}
|
||||
|
||||
if let Some(tx) = config.navigation_tx {
|
||||
win_builder = win_builder.on_navigation(move |url| {
|
||||
let url = url.to_string();
|
||||
let tx = tx.clone();
|
||||
tauri::async_runtime::block_on(async move {
|
||||
tx.send(url).await.unwrap();
|
||||
});
|
||||
true
|
||||
});
|
||||
}
|
||||
|
||||
if config.hide_titlebar {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
win_builder = win_builder.hidden_title(true).title_bar_style(TitleBarStyle::Overlay);
|
||||
}
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
{
|
||||
// Doesn't seem to work from Rust, here, so we do it in main.tsx
|
||||
win_builder = win_builder.decorations(false);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(w) = handle.webview_windows().get(config.label) {
|
||||
info!("Webview with label {} already exists. Focusing existing", config.label);
|
||||
w.set_focus().unwrap();
|
||||
return w.to_owned();
|
||||
}
|
||||
|
||||
let win = win_builder.build().unwrap();
|
||||
|
||||
let webview_window = win.clone();
|
||||
win.on_menu_event(move |w, event| {
|
||||
if !w.is_focused().unwrap() {
|
||||
return;
|
||||
}
|
||||
|
||||
let event_id = event.id().0.as_str();
|
||||
match event_id {
|
||||
"quit" => exit(0),
|
||||
"close" => w.close().unwrap(),
|
||||
"zoom_reset" => w.emit("zoom_reset", true).unwrap(),
|
||||
"zoom_in" => w.emit("zoom_in", true).unwrap(),
|
||||
"zoom_out" => w.emit("zoom_out", true).unwrap(),
|
||||
"settings" => w.emit("settings", true).unwrap(),
|
||||
"open_feedback" => {
|
||||
if let Err(e) =
|
||||
w.app_handle().opener().open_url("https://yaak.app/feedback", None::<&str>)
|
||||
{
|
||||
warn!("Failed to open feedback {e:?}")
|
||||
}
|
||||
}
|
||||
|
||||
// Commands for development
|
||||
"dev.reset_size" => webview_window
|
||||
.set_size(LogicalSize::new(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT))
|
||||
.unwrap(),
|
||||
"dev.refresh" => webview_window.eval("location.reload()").unwrap(),
|
||||
"dev.generate_theme_css" => {
|
||||
w.emit("generate_theme_css", true).unwrap();
|
||||
}
|
||||
"dev.toggle_devtools" => {
|
||||
if webview_window.is_devtools_open() {
|
||||
webview_window.close_devtools();
|
||||
} else {
|
||||
webview_window.open_devtools();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
|
||||
win
|
||||
}
|
||||
@@ -3,9 +3,9 @@ use tauri::menu::{
|
||||
WINDOW_SUBMENU_ID,
|
||||
};
|
||||
pub use tauri::AppHandle;
|
||||
use tauri::Wry;
|
||||
use tauri::Runtime;
|
||||
|
||||
pub fn app_menu(app_handle: &AppHandle) -> tauri::Result<Menu<Wry>> {
|
||||
pub fn app_menu<R: Runtime>(app_handle: &AppHandle<R>) -> tauri::Result<Menu<R>> {
|
||||
let pkg_info = app_handle.package_info();
|
||||
let config = app_handle.config();
|
||||
let about_metadata = AboutMetadata {
|
||||
@@ -37,7 +37,7 @@ pub fn app_menu(app_handle: &AppHandle) -> tauri::Result<Menu<Wry>> {
|
||||
true,
|
||||
&[
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
&PredefinedMenuItem::about(app_handle, None, Some(about_metadata))?,
|
||||
&PredefinedMenuItem::about(app_handle, None, Some(about_metadata.clone()))?,
|
||||
#[cfg(target_os = "macos")]
|
||||
&MenuItemBuilder::with_id("open_feedback".to_string(), "Give Feedback")
|
||||
.build(app_handle)?,
|
||||
|
||||
+3
-3
@@ -28,7 +28,7 @@ var plugin = {
|
||||
name: "basic",
|
||||
label: "Basic Auth",
|
||||
shortLabel: "Basic",
|
||||
config: [{
|
||||
args: [{
|
||||
type: "text",
|
||||
name: "username",
|
||||
label: "Username",
|
||||
@@ -40,8 +40,8 @@ var plugin = {
|
||||
optional: true,
|
||||
password: true
|
||||
}],
|
||||
async onApply(_ctx, args) {
|
||||
const { username, password } = args.config;
|
||||
async onApply(_ctx, { values }) {
|
||||
const { username, password } = values;
|
||||
const value = "Basic " + Buffer.from(`${username}:${password}`).toString("base64");
|
||||
return { setHeaders: [{ name: "Authorization", value }] };
|
||||
}
|
||||
|
||||
+3
-3
@@ -28,15 +28,15 @@ var plugin = {
|
||||
name: "bearer",
|
||||
label: "Bearer Token",
|
||||
shortLabel: "Bearer",
|
||||
config: [{
|
||||
args: [{
|
||||
type: "text",
|
||||
name: "token",
|
||||
label: "Token",
|
||||
optional: true,
|
||||
password: true
|
||||
}],
|
||||
async onApply(_ctx, args) {
|
||||
const { token } = args.config;
|
||||
async onApply(_ctx, { values }) {
|
||||
const { token } = values;
|
||||
const value = `Bearer ${token}`.trim();
|
||||
return { setHeaders: [{ name: "Authorization", value }] };
|
||||
}
|
||||
|
||||
+7
-6
@@ -3814,21 +3814,22 @@ var plugin = {
|
||||
name: "jwt",
|
||||
label: "JWT Bearer",
|
||||
shortLabel: "JWT",
|
||||
config: [
|
||||
args: [
|
||||
{
|
||||
type: "select",
|
||||
name: "algorithm",
|
||||
label: "Algorithm",
|
||||
hideLabel: true,
|
||||
defaultValue: defaultAlgorithm,
|
||||
options: algorithms.map((value) => ({ name: value === "none" ? "None" : value, value }))
|
||||
options: algorithms.map((value) => ({ label: value === "none" ? "None" : value, value }))
|
||||
},
|
||||
{
|
||||
type: "editor",
|
||||
type: "text",
|
||||
name: "secret",
|
||||
label: "Secret or Private Key",
|
||||
password: true,
|
||||
optional: true,
|
||||
hideGutter: true
|
||||
multiLine: true
|
||||
},
|
||||
{
|
||||
type: "checkbox",
|
||||
@@ -3844,8 +3845,8 @@ var plugin = {
|
||||
placeholder: "{ }"
|
||||
}
|
||||
],
|
||||
async onApply(_ctx, args) {
|
||||
const { algorithm, secret: _secret, secretBase64, payload } = args.config;
|
||||
async onApply(_ctx, { values }) {
|
||||
const { algorithm, secret: _secret, secretBase64, payload } = values;
|
||||
const secret = secretBase64 ? Buffer.from(`${_secret}`, "base64") : `${_secret}`;
|
||||
const token = import_jsonwebtoken.default.sign(`${payload}`, secret, { algorithm });
|
||||
const value = `Bearer ${token}`;
|
||||
|
||||
+673
@@ -0,0 +1,673 @@
|
||||
"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);
|
||||
|
||||
// src/grants/authorizationCode.ts
|
||||
var import_node_crypto = require("node:crypto");
|
||||
|
||||
// src/getAccessToken.ts
|
||||
var import_node_fs = require("node:fs");
|
||||
async function getAccessToken(ctx, {
|
||||
accessTokenUrl,
|
||||
scope,
|
||||
params,
|
||||
grantType,
|
||||
credentialsInBody,
|
||||
clientId,
|
||||
clientSecret
|
||||
}) {
|
||||
console.log("Getting access token", accessTokenUrl);
|
||||
const httpRequest = {
|
||||
method: "POST",
|
||||
url: accessTokenUrl,
|
||||
bodyType: "application/x-www-form-urlencoded",
|
||||
body: {
|
||||
form: [
|
||||
{ name: "grant_type", value: grantType },
|
||||
...params
|
||||
]
|
||||
},
|
||||
headers: [
|
||||
{ name: "User-Agent", value: "yaak" },
|
||||
{ name: "Accept", value: "application/x-www-form-urlencoded, application/json" },
|
||||
{ name: "Content-Type", value: "application/x-www-form-urlencoded" }
|
||||
]
|
||||
};
|
||||
if (scope) httpRequest.body.form.push({ name: "scope", value: scope });
|
||||
if (credentialsInBody) {
|
||||
httpRequest.body.form.push({ name: "client_id", value: clientId });
|
||||
httpRequest.body.form.push({ name: "client_secret", value: clientSecret });
|
||||
} else {
|
||||
const value = "Basic " + Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
|
||||
httpRequest.headers.push({ name: "Authorization", value });
|
||||
}
|
||||
const resp = await ctx.httpRequest.send({ httpRequest });
|
||||
if (resp.status < 200 || resp.status >= 300) {
|
||||
throw new Error("Failed to fetch access token with status=" + resp.status);
|
||||
}
|
||||
const body = (0, import_node_fs.readFileSync)(resp.bodyPath ?? "", "utf8");
|
||||
let response;
|
||||
try {
|
||||
response = JSON.parse(body);
|
||||
} catch {
|
||||
response = Object.fromEntries(new URLSearchParams(body));
|
||||
}
|
||||
if (response.error) {
|
||||
throw new Error("Failed to fetch access token with " + response.error);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
// src/getOrRefreshAccessToken.ts
|
||||
var import_node_fs2 = require("node:fs");
|
||||
|
||||
// src/store.ts
|
||||
async function storeToken(ctx, contextId, response) {
|
||||
if (!response.access_token) {
|
||||
throw new Error(`Token not found in response`);
|
||||
}
|
||||
const expiresAt = response.expires_in ? Date.now() + response.expires_in * 1e3 : null;
|
||||
const token = {
|
||||
response,
|
||||
expiresAt
|
||||
};
|
||||
await ctx.store.set(tokenStoreKey(contextId), token);
|
||||
return token;
|
||||
}
|
||||
async function getToken(ctx, contextId) {
|
||||
return ctx.store.get(tokenStoreKey(contextId));
|
||||
}
|
||||
async function deleteToken(ctx, contextId) {
|
||||
return ctx.store.delete(tokenStoreKey(contextId));
|
||||
}
|
||||
function tokenStoreKey(context_id) {
|
||||
return ["token", context_id].join("::");
|
||||
}
|
||||
|
||||
// src/getOrRefreshAccessToken.ts
|
||||
async function getOrRefreshAccessToken(ctx, contextId, {
|
||||
scope,
|
||||
accessTokenUrl,
|
||||
credentialsInBody,
|
||||
clientId,
|
||||
clientSecret,
|
||||
forceRefresh
|
||||
}) {
|
||||
const token = await getToken(ctx, contextId);
|
||||
if (token == null) {
|
||||
return null;
|
||||
}
|
||||
const now = Date.now() / 1e3;
|
||||
const isExpired = token.expiresAt && now > token.expiresAt;
|
||||
if (!isExpired && !forceRefresh) {
|
||||
return token;
|
||||
}
|
||||
if (!token.response.refresh_token) {
|
||||
return null;
|
||||
}
|
||||
const httpRequest = {
|
||||
method: "POST",
|
||||
url: accessTokenUrl,
|
||||
bodyType: "application/x-www-form-urlencoded",
|
||||
body: {
|
||||
form: [
|
||||
{ name: "grant_type", value: "refresh_token" },
|
||||
{ name: "refresh_token", value: token.response.refresh_token }
|
||||
]
|
||||
},
|
||||
headers: [
|
||||
{ name: "User-Agent", value: "yaak" },
|
||||
{ name: "Accept", value: "application/x-www-form-urlencoded, application/json" },
|
||||
{ name: "Content-Type", value: "application/x-www-form-urlencoded" }
|
||||
]
|
||||
};
|
||||
if (scope) httpRequest.body.form.push({ name: "scope", value: scope });
|
||||
if (credentialsInBody) {
|
||||
httpRequest.body.form.push({ name: "client_id", value: clientId });
|
||||
httpRequest.body.form.push({ name: "client_secret", value: clientSecret });
|
||||
} else {
|
||||
const value = "Basic " + Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
|
||||
httpRequest.headers.push({ name: "Authorization", value });
|
||||
}
|
||||
const resp = await ctx.httpRequest.send({ httpRequest });
|
||||
if (resp.status === 401) {
|
||||
console.log("Unauthorized refresh_token request");
|
||||
await deleteToken(ctx, contextId);
|
||||
return null;
|
||||
}
|
||||
if (resp.status < 200 || resp.status >= 300) {
|
||||
throw new Error("Failed to fetch access token with status=" + resp.status);
|
||||
}
|
||||
const body = (0, import_node_fs2.readFileSync)(resp.bodyPath ?? "", "utf8");
|
||||
let response;
|
||||
try {
|
||||
response = JSON.parse(body);
|
||||
} catch {
|
||||
response = Object.fromEntries(new URLSearchParams(body));
|
||||
}
|
||||
if (response.error) {
|
||||
throw new Error(`Failed to fetch access token with ${response.error} -> ${response.error_description}`);
|
||||
}
|
||||
const newResponse = {
|
||||
...response,
|
||||
// Assign a new one or keep the old one,
|
||||
refresh_token: response.refresh_token ?? token.response.refresh_token
|
||||
};
|
||||
return storeToken(ctx, contextId, newResponse);
|
||||
}
|
||||
|
||||
// src/grants/authorizationCode.ts
|
||||
var PKCE_SHA256 = "S256";
|
||||
var PKCE_PLAIN = "plain";
|
||||
var DEFAULT_PKCE_METHOD = PKCE_SHA256;
|
||||
async function getAuthorizationCode(ctx, contextId, {
|
||||
authorizationUrl: authorizationUrlRaw,
|
||||
accessTokenUrl,
|
||||
clientId,
|
||||
clientSecret,
|
||||
redirectUri,
|
||||
scope,
|
||||
state,
|
||||
credentialsInBody,
|
||||
pkce
|
||||
}) {
|
||||
const token = await getOrRefreshAccessToken(ctx, contextId, {
|
||||
accessTokenUrl,
|
||||
scope,
|
||||
clientId,
|
||||
clientSecret,
|
||||
credentialsInBody
|
||||
});
|
||||
if (token != null) {
|
||||
return token;
|
||||
}
|
||||
const authorizationUrl = new URL(`${authorizationUrlRaw ?? ""}`);
|
||||
authorizationUrl.searchParams.set("response_type", "code");
|
||||
authorizationUrl.searchParams.set("client_id", clientId);
|
||||
if (redirectUri) authorizationUrl.searchParams.set("redirect_uri", redirectUri);
|
||||
if (scope) authorizationUrl.searchParams.set("scope", scope);
|
||||
if (state) authorizationUrl.searchParams.set("state", state);
|
||||
if (pkce) {
|
||||
const verifier = pkce.codeVerifier || createPkceCodeVerifier();
|
||||
const challengeMethod = pkce.challengeMethod || DEFAULT_PKCE_METHOD;
|
||||
authorizationUrl.searchParams.set("code_challenge", createPkceCodeChallenge(verifier, challengeMethod));
|
||||
authorizationUrl.searchParams.set("code_challenge_method", challengeMethod);
|
||||
}
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const authorizationUrlStr = authorizationUrl.toString();
|
||||
console.log("Authorizing", authorizationUrlStr);
|
||||
let { close } = await ctx.window.openUrl({
|
||||
url: authorizationUrlStr,
|
||||
label: "oauth-authorization-url",
|
||||
async onNavigate({ url: urlStr }) {
|
||||
const url = new URL(urlStr);
|
||||
if (url.searchParams.has("error")) {
|
||||
return reject(new Error(`Failed to authorize: ${url.searchParams.get("error")}`));
|
||||
}
|
||||
const code = url.searchParams.get("code");
|
||||
if (!code) {
|
||||
return;
|
||||
}
|
||||
close();
|
||||
const response = await getAccessToken(ctx, {
|
||||
grantType: "authorization_code",
|
||||
accessTokenUrl,
|
||||
clientId,
|
||||
clientSecret,
|
||||
scope,
|
||||
credentialsInBody,
|
||||
params: [
|
||||
{ name: "code", value: code },
|
||||
...redirectUri ? [{ name: "redirect_uri", value: redirectUri }] : []
|
||||
]
|
||||
});
|
||||
try {
|
||||
resolve(await storeToken(ctx, contextId, response));
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
function createPkceCodeVerifier() {
|
||||
return encodeForPkce((0, import_node_crypto.randomBytes)(32));
|
||||
}
|
||||
function createPkceCodeChallenge(verifier, method) {
|
||||
if (method === "plain") {
|
||||
return verifier;
|
||||
}
|
||||
const hash = encodeForPkce((0, import_node_crypto.createHash)("sha256").update(verifier).digest());
|
||||
return hash.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
||||
}
|
||||
function encodeForPkce(bytes) {
|
||||
return bytes.toString("base64").replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
||||
}
|
||||
|
||||
// src/grants/clientCredentials.ts
|
||||
async function getClientCredentials(ctx, contextId, {
|
||||
accessTokenUrl,
|
||||
clientId,
|
||||
clientSecret,
|
||||
scope,
|
||||
credentialsInBody
|
||||
}) {
|
||||
const token = await getToken(ctx, contextId);
|
||||
if (token) {
|
||||
}
|
||||
const response = await getAccessToken(ctx, {
|
||||
grantType: "client_credentials",
|
||||
accessTokenUrl,
|
||||
clientId,
|
||||
clientSecret,
|
||||
scope,
|
||||
credentialsInBody,
|
||||
params: []
|
||||
});
|
||||
return storeToken(ctx, contextId, response);
|
||||
}
|
||||
|
||||
// src/grants/implicit.ts
|
||||
function getImplicit(ctx, contextId, {
|
||||
authorizationUrl: authorizationUrlRaw,
|
||||
responseType,
|
||||
clientId,
|
||||
redirectUri,
|
||||
scope,
|
||||
state
|
||||
}) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const token = await getToken(ctx, contextId);
|
||||
if (token) {
|
||||
}
|
||||
const authorizationUrl = new URL(`${authorizationUrlRaw ?? ""}`);
|
||||
authorizationUrl.searchParams.set("response_type", "code");
|
||||
authorizationUrl.searchParams.set("client_id", clientId);
|
||||
if (redirectUri) authorizationUrl.searchParams.set("redirect_uri", redirectUri);
|
||||
if (scope) authorizationUrl.searchParams.set("scope", scope);
|
||||
if (state) authorizationUrl.searchParams.set("state", state);
|
||||
if (responseType.includes("id_token")) {
|
||||
authorizationUrl.searchParams.set("nonce", String(Math.floor(Math.random() * 9999999999999) + 1));
|
||||
}
|
||||
const authorizationUrlStr = authorizationUrl.toString();
|
||||
let { close } = await ctx.window.openUrl({
|
||||
url: authorizationUrlStr,
|
||||
label: "oauth-authorization-url",
|
||||
async onNavigate({ url: urlStr }) {
|
||||
const url = new URL(urlStr);
|
||||
if (url.searchParams.has("error")) {
|
||||
return reject(Error(`Failed to authorize: ${url.searchParams.get("error")}`));
|
||||
}
|
||||
close();
|
||||
const hash = url.hash.slice(1);
|
||||
const params = new URLSearchParams(hash);
|
||||
const idToken = params.get("id_token");
|
||||
if (idToken) {
|
||||
params.set("access_token", idToken);
|
||||
params.delete("id_token");
|
||||
}
|
||||
const response = Object.fromEntries(params);
|
||||
try {
|
||||
resolve(await storeToken(ctx, contextId, response));
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// src/grants/password.ts
|
||||
async function getPassword(ctx, contextId, {
|
||||
accessTokenUrl,
|
||||
clientId,
|
||||
clientSecret,
|
||||
username,
|
||||
password,
|
||||
credentialsInBody,
|
||||
scope
|
||||
}) {
|
||||
const token = await getOrRefreshAccessToken(ctx, contextId, {
|
||||
accessTokenUrl,
|
||||
scope,
|
||||
clientId,
|
||||
clientSecret,
|
||||
credentialsInBody
|
||||
});
|
||||
if (token != null) {
|
||||
return token;
|
||||
}
|
||||
const response = await getAccessToken(ctx, {
|
||||
accessTokenUrl,
|
||||
clientId,
|
||||
clientSecret,
|
||||
scope,
|
||||
grantType: "password",
|
||||
credentialsInBody,
|
||||
params: [
|
||||
{ name: "username", value: username },
|
||||
{ name: "password", value: password }
|
||||
]
|
||||
});
|
||||
return storeToken(ctx, contextId, response);
|
||||
}
|
||||
|
||||
// src/index.ts
|
||||
var grantTypes = [
|
||||
{ label: "Authorization Code", value: "authorization_code" },
|
||||
{ label: "Implicit", value: "implicit" },
|
||||
{ label: "Resource Owner Password Credential", value: "password" },
|
||||
{ label: "Client Credentials", value: "client_credentials" }
|
||||
];
|
||||
var defaultGrantType = grantTypes[0].value;
|
||||
function hiddenIfNot(grantTypes2, ...other) {
|
||||
return (_ctx, { values }) => {
|
||||
const hasGrantType = grantTypes2.find((t) => t === String(values.grantType ?? defaultGrantType));
|
||||
const hasOtherBools = other.every((t) => t(values));
|
||||
const show = hasGrantType && hasOtherBools;
|
||||
return { hidden: !show };
|
||||
};
|
||||
}
|
||||
var authorizationUrls = [
|
||||
"https://github.com/login/oauth/authorize",
|
||||
"https://account.box.com/api/oauth2/authorize",
|
||||
"https://accounts.google.com/o/oauth2/v2/auth",
|
||||
"https://api.imgur.com/oauth2/authorize",
|
||||
"https://bitly.com/oauth/authorize",
|
||||
"https://gitlab.example.com/oauth/authorize",
|
||||
"https://medium.com/m/oauth/authorize",
|
||||
"https://public-api.wordpress.com/oauth2/authorize",
|
||||
"https://slack.com/oauth/authorize",
|
||||
"https://todoist.com/oauth/authorize",
|
||||
"https://www.dropbox.com/oauth2/authorize",
|
||||
"https://www.linkedin.com/oauth/v2/authorization",
|
||||
"https://MY_SHOP.myshopify.com/admin/oauth/access_token"
|
||||
];
|
||||
var accessTokenUrls = [
|
||||
"https://github.com/login/oauth/access_token",
|
||||
"https://api-ssl.bitly.com/oauth/access_token",
|
||||
"https://api.box.com/oauth2/token",
|
||||
"https://api.dropboxapi.com/oauth2/token",
|
||||
"https://api.imgur.com/oauth2/token",
|
||||
"https://api.medium.com/v1/tokens",
|
||||
"https://gitlab.example.com/oauth/token",
|
||||
"https://public-api.wordpress.com/oauth2/token",
|
||||
"https://slack.com/api/oauth.access",
|
||||
"https://todoist.com/oauth/access_token",
|
||||
"https://www.googleapis.com/oauth2/v4/token",
|
||||
"https://www.linkedin.com/oauth/v2/accessToken",
|
||||
"https://MY_SHOP.myshopify.com/admin/oauth/authorize"
|
||||
];
|
||||
var plugin = {
|
||||
authentication: {
|
||||
name: "oauth2",
|
||||
label: "OAuth 2.0",
|
||||
shortLabel: "OAuth 2",
|
||||
actions: [
|
||||
{
|
||||
label: "Copy Current Token",
|
||||
name: "copyCurrentToken",
|
||||
icon: "copy",
|
||||
async onSelect(ctx, { contextId }) {
|
||||
const token = await getToken(ctx, contextId);
|
||||
if (token == null) {
|
||||
await ctx.toast.show({ message: "No token to copy", color: "warning" });
|
||||
} else {
|
||||
await ctx.clipboard.copyText(token.response.access_token);
|
||||
await ctx.toast.show({ message: "Token copied to clipboard", icon: "copy", color: "success" });
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Delete Token",
|
||||
name: "clearToken",
|
||||
icon: "trash",
|
||||
async onSelect(ctx, { contextId }) {
|
||||
if (await deleteToken(ctx, contextId)) {
|
||||
await ctx.toast.show({ message: "Token deleted", color: "success" });
|
||||
} else {
|
||||
await ctx.toast.show({ message: "No token to delete", color: "warning" });
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
args: [
|
||||
{
|
||||
type: "select",
|
||||
name: "grantType",
|
||||
label: "Grant Type",
|
||||
hideLabel: true,
|
||||
defaultValue: defaultGrantType,
|
||||
options: grantTypes
|
||||
},
|
||||
// Always-present fields
|
||||
{ type: "text", name: "clientId", label: "Client ID" },
|
||||
{
|
||||
type: "text",
|
||||
name: "clientSecret",
|
||||
label: "Client Secret",
|
||||
password: true,
|
||||
dynamic: hiddenIfNot(["authorization_code", "password", "client_credentials"])
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
name: "authorizationUrl",
|
||||
label: "Authorization URL",
|
||||
dynamic: hiddenIfNot(["authorization_code", "implicit"]),
|
||||
placeholder: authorizationUrls[0],
|
||||
completionOptions: authorizationUrls.map((url) => ({ label: url, value: url }))
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
name: "accessTokenUrl",
|
||||
label: "Access Token URL",
|
||||
placeholder: accessTokenUrls[0],
|
||||
dynamic: hiddenIfNot(["authorization_code", "password", "client_credentials"]),
|
||||
completionOptions: accessTokenUrls.map((url) => ({ label: url, value: url }))
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
name: "redirectUri",
|
||||
label: "Redirect URI",
|
||||
optional: true,
|
||||
dynamic: hiddenIfNot(["authorization_code", "implicit"])
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
name: "state",
|
||||
label: "State",
|
||||
optional: true,
|
||||
dynamic: hiddenIfNot(["authorization_code", "implicit"])
|
||||
},
|
||||
{
|
||||
type: "checkbox",
|
||||
name: "usePkce",
|
||||
label: "Use PKCE",
|
||||
dynamic: hiddenIfNot(["authorization_code"])
|
||||
},
|
||||
{
|
||||
type: "select",
|
||||
name: "pkceChallengeMethod",
|
||||
label: "Code Challenge Method",
|
||||
options: [{ label: "SHA-256", value: PKCE_SHA256 }, { label: "Plain", value: PKCE_PLAIN }],
|
||||
defaultValue: DEFAULT_PKCE_METHOD,
|
||||
dynamic: hiddenIfNot(["authorization_code"], ({ usePkce }) => !!usePkce)
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
name: "pkceCodeVerifier",
|
||||
label: "Code Verifier",
|
||||
placeholder: "Automatically generated if not provided",
|
||||
optional: true,
|
||||
dynamic: hiddenIfNot(["authorization_code"], ({ usePkce }) => !!usePkce)
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
name: "username",
|
||||
label: "Username",
|
||||
optional: true,
|
||||
dynamic: hiddenIfNot(["password"])
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
name: "password",
|
||||
label: "Password",
|
||||
password: true,
|
||||
optional: true,
|
||||
dynamic: hiddenIfNot(["password"])
|
||||
},
|
||||
{
|
||||
type: "select",
|
||||
name: "responseType",
|
||||
label: "Response Type",
|
||||
defaultValue: "token",
|
||||
options: [
|
||||
{ label: "Access Token", value: "token" },
|
||||
{ label: "ID Token", value: "id_token" },
|
||||
{ label: "ID and Access Token", value: "id_token token" }
|
||||
],
|
||||
dynamic: hiddenIfNot(["implicit"])
|
||||
},
|
||||
{
|
||||
type: "accordion",
|
||||
label: "Advanced",
|
||||
inputs: [
|
||||
{ type: "text", name: "scope", label: "Scope", optional: true },
|
||||
{ type: "text", name: "headerPrefix", label: "Header Prefix", optional: true, defaultValue: "Bearer" },
|
||||
{
|
||||
type: "select",
|
||||
name: "credentials",
|
||||
label: "Send Credentials",
|
||||
defaultValue: "body",
|
||||
options: [
|
||||
{ label: "In Request Body", value: "body" },
|
||||
{ label: "As Basic Authentication", value: "basic" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "accordion",
|
||||
label: "Access Token Response",
|
||||
async dynamic(ctx, { contextId }) {
|
||||
const token = await getToken(ctx, contextId);
|
||||
if (token == null) {
|
||||
return { hidden: true };
|
||||
}
|
||||
return {
|
||||
label: "Access Token Response",
|
||||
inputs: [
|
||||
{
|
||||
type: "editor",
|
||||
defaultValue: JSON.stringify(token.response, null, 2),
|
||||
hideLabel: true,
|
||||
readOnly: true,
|
||||
language: "json"
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
||||
],
|
||||
async onApply(ctx, { values, contextId }) {
|
||||
const headerPrefix = optionalString(values, "headerPrefix") ?? "";
|
||||
const grantType = requiredString(values, "grantType");
|
||||
const credentialsInBody = values.credentials === "body";
|
||||
console.log("Performing OAuth", values);
|
||||
let token;
|
||||
if (grantType === "authorization_code") {
|
||||
const authorizationUrl = requiredString(values, "authorizationUrl");
|
||||
const accessTokenUrl = requiredString(values, "accessTokenUrl");
|
||||
token = await getAuthorizationCode(ctx, contextId, {
|
||||
accessTokenUrl: accessTokenUrl.match(/^https?:\/\//) ? accessTokenUrl : `https://${accessTokenUrl}`,
|
||||
authorizationUrl: authorizationUrl.match(/^https?:\/\//) ? authorizationUrl : `https://${authorizationUrl}`,
|
||||
clientId: requiredString(values, "clientId"),
|
||||
clientSecret: requiredString(values, "clientSecret"),
|
||||
redirectUri: optionalString(values, "redirectUri"),
|
||||
scope: optionalString(values, "scope"),
|
||||
state: optionalString(values, "state"),
|
||||
credentialsInBody,
|
||||
pkce: values.usePkce ? {
|
||||
challengeMethod: requiredString(values, "pkceChallengeMethod"),
|
||||
codeVerifier: optionalString(values, "pkceCodeVerifier")
|
||||
} : null
|
||||
});
|
||||
} else if (grantType === "implicit") {
|
||||
const authorizationUrl = requiredString(values, "authorizationUrl");
|
||||
token = await getImplicit(ctx, contextId, {
|
||||
authorizationUrl: authorizationUrl.match(/^https?:\/\//) ? authorizationUrl : `https://${authorizationUrl}`,
|
||||
clientId: requiredString(values, "clientId"),
|
||||
redirectUri: optionalString(values, "redirectUri"),
|
||||
responseType: requiredString(values, "responseType"),
|
||||
scope: optionalString(values, "scope"),
|
||||
state: optionalString(values, "state")
|
||||
});
|
||||
} else if (grantType === "client_credentials") {
|
||||
const accessTokenUrl = requiredString(values, "accessTokenUrl");
|
||||
token = await getClientCredentials(ctx, contextId, {
|
||||
accessTokenUrl: accessTokenUrl.match(/^https?:\/\//) ? accessTokenUrl : `https://${accessTokenUrl}`,
|
||||
clientId: requiredString(values, "clientId"),
|
||||
clientSecret: requiredString(values, "clientSecret"),
|
||||
scope: optionalString(values, "scope"),
|
||||
credentialsInBody
|
||||
});
|
||||
} else if (grantType === "password") {
|
||||
const accessTokenUrl = requiredString(values, "accessTokenUrl");
|
||||
token = await getPassword(ctx, contextId, {
|
||||
accessTokenUrl: accessTokenUrl.match(/^https?:\/\//) ? accessTokenUrl : `https://${accessTokenUrl}`,
|
||||
clientId: requiredString(values, "clientId"),
|
||||
clientSecret: requiredString(values, "clientSecret"),
|
||||
username: requiredString(values, "username"),
|
||||
password: requiredString(values, "password"),
|
||||
scope: optionalString(values, "scope"),
|
||||
credentialsInBody
|
||||
});
|
||||
} else {
|
||||
throw new Error("Invalid grant type " + grantType);
|
||||
}
|
||||
const headerValue = `${headerPrefix} ${token.response.access_token}`.trim();
|
||||
return {
|
||||
setHeaders: [{
|
||||
name: "Authorization",
|
||||
value: headerValue
|
||||
}]
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
function optionalString(values, name) {
|
||||
const arg = values[name];
|
||||
if (arg == null || arg == "") return null;
|
||||
return `${arg}`;
|
||||
}
|
||||
function requiredString(values, name) {
|
||||
const arg = optionalString(values, name);
|
||||
if (!arg) throw new Error(`Missing required argument ${name}`);
|
||||
return arg;
|
||||
}
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
plugin
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "@yaakapp/auth-oauth2",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"build": "yaakcli build ./src/index.ts",
|
||||
"dev": "yaakcli dev ./src/index.js"
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -27,14 +27,14 @@ module.exports = __toCommonJS(src_exports);
|
||||
var NEWLINE = "\\\n ";
|
||||
var plugin = {
|
||||
httpRequestActions: [{
|
||||
key: "export-curl",
|
||||
name: "export-curl",
|
||||
label: "Copy as Curl",
|
||||
icon: "copy",
|
||||
async onSelect(ctx, args) {
|
||||
const rendered_request = await ctx.httpRequest.render({ httpRequest: args.httpRequest, purpose: "preview" });
|
||||
const data = await convertToCurl(rendered_request);
|
||||
ctx.clipboard.copyText(data);
|
||||
ctx.toast.show({ message: "Curl copied to clipboard", icon: "copy" });
|
||||
await ctx.clipboard.copyText(data);
|
||||
await ctx.toast.show({ message: "Curl copied to clipboard", icon: "copy", color: "success" });
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
@@ -8824,8 +8824,8 @@ var behaviorArg = {
|
||||
label: "Sending Behavior",
|
||||
defaultValue: "smart",
|
||||
options: [
|
||||
{ name: "When no responses", value: "smart" },
|
||||
{ name: "Always", value: "always" }
|
||||
{ label: "When no responses", value: "smart" },
|
||||
{ label: "Always", value: "always" }
|
||||
]
|
||||
};
|
||||
var requestArg = {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type CheckActivationRequestPayload = { activationId: string, };
|
||||
@@ -15,7 +15,7 @@ const TRIAL_SECONDS: u64 = 3600 * 24 * 14;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct CheckActivationRequestPayload {
|
||||
pub activation_id: String,
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ sea-query = { version = "0.32.1", features = ["with-chrono", "attr"] }
|
||||
sea-query-rusqlite = { version = "0.7.0", features = ["with-chrono"] }
|
||||
serde = { version = "1.0.204", features = ["derive"] }
|
||||
serde_json = "1.0.122"
|
||||
sqlx = { version = "0.8.0", features = ["sqlite", "runtime-tokio-rustls"] }
|
||||
sqlx = { version = "0.8.0", default-features = false, features = ["migrate", "sqlite", "runtime-tokio-rustls"] }
|
||||
tauri = { workspace = true }
|
||||
thiserror = "1.0.63"
|
||||
thiserror = "2.0.11"
|
||||
ts-rs = { workspace = true, features = ["chrono-impl", "serde-json-impl"] }
|
||||
|
||||
@@ -1 +1 @@
|
||||
export * from './bindings/models';
|
||||
export * from './bindings/gen_models';
|
||||
|
||||
@@ -10,7 +10,7 @@ use ts_rs::TS;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "camelCase", tag = "type")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub enum ProxySetting {
|
||||
Enabled {
|
||||
http: String,
|
||||
@@ -22,7 +22,7 @@ pub enum ProxySetting {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct ProxySettingAuth {
|
||||
pub user: String,
|
||||
pub password: String,
|
||||
@@ -30,7 +30,7 @@ pub struct ProxySettingAuth {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub enum EditorKeymap {
|
||||
Default,
|
||||
Vim,
|
||||
@@ -72,7 +72,7 @@ impl Default for EditorKeymap {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct Settings {
|
||||
#[ts(type = "\"settings\"")]
|
||||
pub model: String,
|
||||
@@ -149,7 +149,7 @@ impl<'s> TryFrom<&Row<'s>> for Settings {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct Workspace {
|
||||
#[ts(type = "\"workspace\"")]
|
||||
pub model: String,
|
||||
@@ -215,7 +215,7 @@ impl Workspace {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct WorkspaceMeta {
|
||||
#[ts(type = "\"workspace_meta\"")]
|
||||
pub model: String,
|
||||
@@ -255,7 +255,7 @@ impl<'s> TryFrom<&Row<'s>> for WorkspaceMeta {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
enum CookieDomain {
|
||||
HostOnly(String),
|
||||
Suffix(String),
|
||||
@@ -264,14 +264,14 @@ enum CookieDomain {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
enum CookieExpires {
|
||||
AtUtc(String),
|
||||
SessionEnd,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct Cookie {
|
||||
raw_cookie: String,
|
||||
domain: CookieDomain,
|
||||
@@ -281,7 +281,7 @@ pub struct Cookie {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct CookieJar {
|
||||
#[ts(type = "\"cookie_jar\"")]
|
||||
pub model: String,
|
||||
@@ -327,7 +327,7 @@ impl<'s> TryFrom<&Row<'s>> for CookieJar {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct Environment {
|
||||
#[ts(type = "\"environment\"")]
|
||||
pub model: String,
|
||||
@@ -376,7 +376,7 @@ impl<'s> TryFrom<&Row<'s>> for Environment {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct EnvironmentVariable {
|
||||
#[serde(default = "default_true")]
|
||||
#[ts(optional, as = "Option<bool>")]
|
||||
@@ -389,7 +389,7 @@ pub struct EnvironmentVariable {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct Folder {
|
||||
#[ts(type = "\"folder\"")]
|
||||
pub model: String,
|
||||
@@ -440,7 +440,7 @@ impl<'s> TryFrom<&Row<'s>> for Folder {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct HttpRequestHeader {
|
||||
#[serde(default = "default_true")]
|
||||
#[ts(optional, as = "Option<bool>")]
|
||||
@@ -453,7 +453,7 @@ pub struct HttpRequestHeader {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct HttpUrlParameter {
|
||||
#[serde(default = "default_true")]
|
||||
#[ts(optional, as = "Option<bool>")]
|
||||
@@ -466,7 +466,7 @@ pub struct HttpUrlParameter {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct HttpRequest {
|
||||
#[ts(type = "\"http_request\"")]
|
||||
pub model: String,
|
||||
@@ -548,7 +548,7 @@ impl<'s> TryFrom<&Row<'s>> for HttpRequest {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct HttpResponseHeader {
|
||||
pub name: String,
|
||||
pub value: String,
|
||||
@@ -556,7 +556,7 @@ pub struct HttpResponseHeader {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub enum HttpResponseState {
|
||||
Initialized,
|
||||
Connected,
|
||||
@@ -571,7 +571,7 @@ impl Default for HttpResponseState {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct HttpResponse {
|
||||
#[ts(type = "\"http_response\"")]
|
||||
pub model: String,
|
||||
@@ -660,7 +660,7 @@ impl HttpResponse {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct GrpcMetadataEntry {
|
||||
#[serde(default = "default_true")]
|
||||
#[ts(optional, as = "Option<bool>")]
|
||||
@@ -673,7 +673,7 @@ pub struct GrpcMetadataEntry {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct GrpcRequest {
|
||||
#[ts(type = "\"grpc_request\"")]
|
||||
pub model: String,
|
||||
@@ -748,7 +748,7 @@ impl<'s> TryFrom<&Row<'s>> for GrpcRequest {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub enum GrpcConnectionState {
|
||||
Initialized,
|
||||
Connected,
|
||||
@@ -763,7 +763,7 @@ impl Default for GrpcConnectionState {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct GrpcConnection {
|
||||
#[ts(type = "\"grpc_connection\"")]
|
||||
pub model: String,
|
||||
@@ -831,7 +831,7 @@ impl<'s> TryFrom<&Row<'s>> for GrpcConnection {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub enum GrpcEventType {
|
||||
Info,
|
||||
Error,
|
||||
@@ -849,7 +849,7 @@ impl Default for GrpcEventType {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct GrpcEvent {
|
||||
#[ts(type = "\"grpc_event\"")]
|
||||
pub model: String,
|
||||
@@ -911,7 +911,7 @@ impl<'s> TryFrom<&Row<'s>> for GrpcEvent {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct Plugin {
|
||||
#[ts(type = "\"plugin\"")]
|
||||
pub model: String,
|
||||
@@ -959,7 +959,7 @@ impl<'s> TryFrom<&Row<'s>> for Plugin {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct SyncState {
|
||||
#[ts(type = "\"sync_state\"")]
|
||||
pub model: String,
|
||||
@@ -977,7 +977,7 @@ pub struct SyncState {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct SyncHistory {
|
||||
#[ts(type = "\"sync_history\"")]
|
||||
pub model: String,
|
||||
@@ -1029,7 +1029,7 @@ impl<'s> TryFrom<&Row<'s>> for SyncState {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct KeyValue {
|
||||
#[ts(type = "\"key_value\"")]
|
||||
pub model: String,
|
||||
@@ -1069,6 +1069,48 @@ impl<'s> TryFrom<&Row<'s>> for KeyValue {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct PluginKeyValue {
|
||||
#[ts(type = "\"plugin_key_value\"")]
|
||||
pub model: String,
|
||||
pub created_at: NaiveDateTime,
|
||||
pub updated_at: NaiveDateTime,
|
||||
|
||||
pub plugin_name: String,
|
||||
pub key: String,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
#[derive(Iden)]
|
||||
pub enum PluginKeyValueIden {
|
||||
#[iden = "plugin_key_values"]
|
||||
Table,
|
||||
Model,
|
||||
CreatedAt,
|
||||
UpdatedAt,
|
||||
|
||||
PluginName,
|
||||
Key,
|
||||
Value,
|
||||
}
|
||||
|
||||
impl<'s> TryFrom<&Row<'s>> for PluginKeyValue {
|
||||
type Error = rusqlite::Error;
|
||||
|
||||
fn try_from(r: &Row<'s>) -> Result<Self, Self::Error> {
|
||||
Ok(PluginKeyValue {
|
||||
model: r.get("model")?,
|
||||
created_at: r.get("created_at")?,
|
||||
updated_at: r.get("updated_at")?,
|
||||
plugin_name: r.get("plugin_name")?,
|
||||
key: r.get("key")?,
|
||||
value: r.get("value")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn default_true() -> bool {
|
||||
true
|
||||
}
|
||||
@@ -1114,7 +1156,7 @@ impl ModelType {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase", untagged)]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub enum AnyModel {
|
||||
CookieJar(CookieJar),
|
||||
Environment(Environment),
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
use crate::error::Error::ModelNotFound;
|
||||
use crate::error::Result;
|
||||
use crate::models::{
|
||||
AnyModel, CookieJar, CookieJarIden, Environment, EnvironmentIden, Folder, FolderIden,
|
||||
GrpcConnection, GrpcConnectionIden, GrpcConnectionState, GrpcEvent, GrpcEventIden, GrpcRequest,
|
||||
GrpcRequestIden, HttpRequest, HttpRequestIden, HttpResponse, HttpResponseHeader,
|
||||
HttpResponseIden, HttpResponseState, KeyValue, KeyValueIden, ModelType, Plugin, PluginIden,
|
||||
Settings, SettingsIden, SyncState, SyncStateIden, Workspace, WorkspaceIden, WorkspaceMeta,
|
||||
WorkspaceMetaIden,
|
||||
};
|
||||
use crate::models::{AnyModel, CookieJar, CookieJarIden, Environment, EnvironmentIden, Folder, FolderIden, GrpcConnection, GrpcConnectionIden, GrpcConnectionState, GrpcEvent, GrpcEventIden, GrpcRequest, GrpcRequestIden, HttpRequest, HttpRequestIden, HttpResponse, HttpResponseHeader, HttpResponseIden, HttpResponseState, KeyValue, KeyValueIden, ModelType, Plugin, PluginIden, PluginKeyValue, PluginKeyValueIden, Settings, SettingsIden, SyncState, SyncStateIden, Workspace, WorkspaceIden, WorkspaceMeta, WorkspaceMetaIden};
|
||||
use crate::plugin::SqliteConnection;
|
||||
use chrono::{NaiveDateTime, Utc};
|
||||
use log::{debug, error, info, warn};
|
||||
@@ -165,6 +158,90 @@ pub async fn get_key_value_raw<R: Runtime>(
|
||||
db.query_row(sql.as_str(), &*params.as_params(), |row| row.try_into()).ok()
|
||||
}
|
||||
|
||||
pub async fn get_plugin_key_value<R: Runtime>(
|
||||
mgr: &impl Manager<R>,
|
||||
plugin_name: &str,
|
||||
key: &str,
|
||||
) -> Option<PluginKeyValue> {
|
||||
let dbm = &*mgr.state::<SqliteConnection>();
|
||||
let db = dbm.0.lock().await.get().unwrap();
|
||||
let (sql, params) = Query::select()
|
||||
.from(PluginKeyValueIden::Table)
|
||||
.column(Asterisk)
|
||||
.cond_where(
|
||||
Cond::all()
|
||||
.add(Expr::col(PluginKeyValueIden::PluginName).eq(plugin_name))
|
||||
.add(Expr::col(PluginKeyValueIden::Key).eq(key)),
|
||||
)
|
||||
.build_rusqlite(SqliteQueryBuilder);
|
||||
|
||||
db.query_row(sql.as_str(), &*params.as_params(), |row| row.try_into()).ok()
|
||||
}
|
||||
|
||||
pub async fn set_plugin_key_value<R: Runtime>(
|
||||
mgr: &impl Manager<R>,
|
||||
plugin_name: &str,
|
||||
key: &str,
|
||||
value: &str,
|
||||
) -> (PluginKeyValue, bool) {
|
||||
let existing = get_plugin_key_value(mgr, plugin_name, key).await;
|
||||
|
||||
let dbm = &*mgr.state::<SqliteConnection>();
|
||||
let db = dbm.0.lock().await.get().unwrap();
|
||||
let (sql, params) = Query::insert()
|
||||
.into_table(PluginKeyValueIden::Table)
|
||||
.columns([
|
||||
PluginKeyValueIden::CreatedAt,
|
||||
PluginKeyValueIden::UpdatedAt,
|
||||
PluginKeyValueIden::PluginName,
|
||||
PluginKeyValueIden::Key,
|
||||
PluginKeyValueIden::Value,
|
||||
])
|
||||
.values_panic([
|
||||
CurrentTimestamp.into(),
|
||||
CurrentTimestamp.into(),
|
||||
plugin_name.into(),
|
||||
key.into(),
|
||||
value.into(),
|
||||
])
|
||||
.on_conflict(
|
||||
OnConflict::new()
|
||||
.update_columns([PluginKeyValueIden::UpdatedAt, PluginKeyValueIden::Value])
|
||||
.to_owned(),
|
||||
)
|
||||
.returning_all()
|
||||
.build_rusqlite(SqliteQueryBuilder);
|
||||
|
||||
let mut stmt = db.prepare(sql.as_str()).expect("Failed to prepare PluginKeyValue upsert");
|
||||
let m: PluginKeyValue = stmt
|
||||
.query_row(&*params.as_params(), |row| row.try_into())
|
||||
.expect("Failed to upsert KeyValue");
|
||||
(m, existing.is_none())
|
||||
}
|
||||
|
||||
pub async fn delete_plugin_key_value<R: Runtime>(
|
||||
mgr: &impl Manager<R>,
|
||||
plugin_name: &str,
|
||||
key: &str,
|
||||
) -> bool {
|
||||
if let None = get_plugin_key_value(mgr, plugin_name, key).await {
|
||||
return false;
|
||||
}
|
||||
|
||||
let dbm = &*mgr.state::<SqliteConnection>();
|
||||
let db = dbm.0.lock().await.get().unwrap();
|
||||
let (sql, params) = Query::delete()
|
||||
.from_table(PluginKeyValueIden::Table)
|
||||
.cond_where(
|
||||
Cond::all()
|
||||
.add(Expr::col(PluginKeyValueIden::PluginName).eq(plugin_name))
|
||||
.add(Expr::col(PluginKeyValueIden::Key).eq(key)),
|
||||
)
|
||||
.build_rusqlite(SqliteQueryBuilder);
|
||||
db.execute(sql.as_str(), &*params.as_params()).expect("Failed to delete PluginKeyValue");
|
||||
true
|
||||
}
|
||||
|
||||
pub async fn list_workspaces<R: Runtime>(mgr: &impl Manager<R>) -> Result<Vec<Workspace>> {
|
||||
let dbm = &*mgr.state::<SqliteConnection>();
|
||||
let db = dbm.0.lock().await.get().unwrap();
|
||||
@@ -1999,7 +2076,7 @@ pub fn generate_id() -> String {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct ModelPayload {
|
||||
pub model: AnyModel,
|
||||
pub window_label: String,
|
||||
@@ -2008,7 +2085,7 @@ pub struct ModelPayload {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub enum UpdateSource {
|
||||
Sync,
|
||||
Window,
|
||||
|
||||
@@ -6,17 +6,18 @@ publish = false
|
||||
|
||||
[dependencies]
|
||||
dunce = "1.0.4"
|
||||
futures-util = "0.3.30"
|
||||
log = "0.4.21"
|
||||
md5 = "0.7.0"
|
||||
path-slash = "0.2.1"
|
||||
rand = "0.8.5"
|
||||
regex = "1.10.6"
|
||||
serde = { version = "1.0.198", features = ["derive"] }
|
||||
serde_json = "1.0.113"
|
||||
tauri = { workspace = true }
|
||||
tauri-plugin-shell = { workspace = true }
|
||||
tokio = { version = "1.42.0", features = ["macros", "rt-multi-thread", "process"] }
|
||||
ts-rs = { workspace = true, features = ["import-esm"] }
|
||||
thiserror = "2.0.7"
|
||||
yaak-models = { workspace = true }
|
||||
regex = "1.10.6"
|
||||
path-slash = "0.2.1"
|
||||
tokio = { version = "1.42.0", features = ["macros", "rt-multi-thread", "process"] }
|
||||
tokio-tungstenite = "0.26.1"
|
||||
futures-util = "0.3.30"
|
||||
ts-rs = { workspace = true, features = ["import-esm"] }
|
||||
yaak-models = { workspace = true }
|
||||
|
||||
@@ -1,290 +0,0 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { Environment } from "./models.js";
|
||||
import type { Folder } from "./models.js";
|
||||
import type { GrpcRequest } from "./models.js";
|
||||
import type { HttpRequest } from "./models.js";
|
||||
import type { HttpResponse } from "./models.js";
|
||||
import type { JsonValue } from "./serde_json/JsonValue.js";
|
||||
import type { Workspace } from "./models.js";
|
||||
|
||||
export type BootRequest = { dir: string, watch: boolean, };
|
||||
|
||||
export type BootResponse = { name: string, version: string, };
|
||||
|
||||
export type CallHttpAuthenticationRequest = { config: { [key in string]?: JsonValue }, method: string, url: string, headers: Array<HttpHeader>, };
|
||||
|
||||
export type CallHttpAuthenticationResponse = {
|
||||
/**
|
||||
* HTTP headers to add to the request. Existing headers will be replaced, while
|
||||
* new headers will be added.
|
||||
*/
|
||||
setHeaders: Array<HttpHeader>, };
|
||||
|
||||
export type CallHttpRequestActionArgs = { httpRequest: HttpRequest, };
|
||||
|
||||
export type CallHttpRequestActionRequest = { key: string, pluginRefId: string, args: CallHttpRequestActionArgs, };
|
||||
|
||||
export type CallTemplateFunctionArgs = { purpose: RenderPurpose, values: { [key in string]?: string }, };
|
||||
|
||||
export type CallTemplateFunctionRequest = { name: string, args: CallTemplateFunctionArgs, };
|
||||
|
||||
export type CallTemplateFunctionResponse = { value: string | null, };
|
||||
|
||||
export type Color = "custom" | "default" | "primary" | "secondary" | "info" | "success" | "notice" | "warning" | "danger";
|
||||
|
||||
export type CopyTextRequest = { text: string, };
|
||||
|
||||
export type EditorLanguage = "text" | "javascript" | "json" | "html" | "xml" | "graphql" | "markdown";
|
||||
|
||||
export type EmptyPayload = {};
|
||||
|
||||
export type ErrorResponse = { error: string, };
|
||||
|
||||
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, };
|
||||
|
||||
export type FindHttpResponsesRequest = { requestId: string, limit?: number, };
|
||||
|
||||
export type FindHttpResponsesResponse = { httpResponses: Array<HttpResponse>, };
|
||||
|
||||
export type FormInput = { "type": "text" } & FormInputText | { "type": "editor" } & FormInputEditor | { "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,
|
||||
/**
|
||||
* Visually hide the label of the input
|
||||
*/
|
||||
hideLabel?: boolean,
|
||||
/**
|
||||
* 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,
|
||||
/**
|
||||
* Visually hide the label of the input
|
||||
*/
|
||||
hideLabel?: boolean,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, };
|
||||
|
||||
export type FormInputEditor = {
|
||||
/**
|
||||
* Placeholder for the text input
|
||||
*/
|
||||
placeholder?: string | null,
|
||||
/**
|
||||
* Don't show the editor gutter (line numbers, folds, etc.)
|
||||
*/
|
||||
hideGutter?: boolean,
|
||||
/**
|
||||
* Language for syntax highlighting
|
||||
*/
|
||||
language?: EditorLanguage, name: string,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
/**
|
||||
* Visually hide the label of the input
|
||||
*/
|
||||
hideLabel?: boolean,
|
||||
/**
|
||||
* 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,
|
||||
/**
|
||||
* Visually hide the label of the input
|
||||
*/
|
||||
hideLabel?: boolean,
|
||||
/**
|
||||
* 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,
|
||||
/**
|
||||
* Visually hide the label of the input
|
||||
*/
|
||||
hideLabel?: boolean,
|
||||
/**
|
||||
* 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,
|
||||
/**
|
||||
* Visually hide the label of the input
|
||||
*/
|
||||
hideLabel?: boolean,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, };
|
||||
|
||||
export type FormInputSelectOption = { name: string, value: string, };
|
||||
|
||||
export type FormInputText = {
|
||||
/**
|
||||
* Placeholder for the text input
|
||||
*/
|
||||
placeholder?: string | null,
|
||||
/**
|
||||
* Placeholder for the text input
|
||||
*/
|
||||
password?: boolean, name: string,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
/**
|
||||
* Visually hide the label of the input
|
||||
*/
|
||||
hideLabel?: boolean,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, };
|
||||
|
||||
export type GetHttpAuthenticationResponse = { name: string, label: string, shortLabel: string, config: Array<FormInput>, };
|
||||
|
||||
export type GetHttpRequestActionsRequest = Record<string, never>;
|
||||
|
||||
export type GetHttpRequestActionsResponse = { actions: Array<HttpRequestAction>, pluginRefId: string, };
|
||||
|
||||
export type GetHttpRequestByIdRequest = { id: string, };
|
||||
|
||||
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";
|
||||
|
||||
export type ImportRequest = { content: string, };
|
||||
|
||||
export type ImportResources = { workspaces: Array<Workspace>, environments: Array<Environment>, folders: Array<Folder>, httpRequests: Array<HttpRequest>, grpcRequests: Array<GrpcRequest>, };
|
||||
|
||||
export type ImportResponse = { resources: ImportResources, };
|
||||
|
||||
export type InternalEvent = { id: string, pluginRefId: string, pluginName: string, replyId: string | null, windowContext: WindowContext, payload: InternalEventPayload, };
|
||||
|
||||
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 | { "type": "error_response" } & ErrorResponse;
|
||||
|
||||
export type PromptTextRequest = { id: string, title: string, label: string, description?: string, defaultValue?: string, placeholder?: string,
|
||||
/**
|
||||
* Text to add to the confirmation button
|
||||
*/
|
||||
confirmText?: string,
|
||||
/**
|
||||
* Text to add to the cancel button
|
||||
*/
|
||||
cancelText?: string,
|
||||
/**
|
||||
* Require the user to enter a non-empty value
|
||||
*/
|
||||
required?: boolean, };
|
||||
|
||||
export type PromptTextResponse = { value: string | null, };
|
||||
|
||||
export type RenderHttpRequestRequest = { httpRequest: HttpRequest, purpose: RenderPurpose, };
|
||||
|
||||
export type RenderHttpRequestResponse = { httpRequest: HttpRequest, };
|
||||
|
||||
export type RenderPurpose = "send" | "preview";
|
||||
|
||||
export type SendHttpRequestRequest = { httpRequest: HttpRequest, };
|
||||
|
||||
export type SendHttpRequestResponse = { httpResponse: HttpResponse, };
|
||||
|
||||
export type ShowToastRequest = { message: string, color?: Color, icon?: Icon, };
|
||||
|
||||
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<FormInput>, };
|
||||
|
||||
export type TemplateRenderRequest = { data: JsonValue, purpose: RenderPurpose, };
|
||||
|
||||
export type TemplateRenderResponse = { data: JsonValue, };
|
||||
|
||||
export type WindowContext = { "type": "none" } | { "type": "label", label: string, };
|
||||
@@ -0,0 +1,405 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { Environment } from "./gen_models.js";
|
||||
import type { Folder } from "./gen_models.js";
|
||||
import type { GrpcRequest } from "./gen_models.js";
|
||||
import type { HttpRequest } from "./gen_models.js";
|
||||
import type { HttpResponse } from "./gen_models.js";
|
||||
import type { JsonValue } from "./serde_json/JsonValue.js";
|
||||
import type { Workspace } from "./gen_models.js";
|
||||
|
||||
export type BootRequest = { dir: string, watch: boolean, };
|
||||
|
||||
export type BootResponse = { name: string, version: string, };
|
||||
|
||||
export type CallHttpAuthenticationActionArgs = { contextId: string, values: { [key in string]?: JsonPrimitive }, };
|
||||
|
||||
export type CallHttpAuthenticationActionRequest = { index: number, pluginRefId: string, args: CallHttpAuthenticationActionArgs, };
|
||||
|
||||
export type CallHttpAuthenticationRequest = { contextId: string, values: { [key in string]?: JsonPrimitive }, method: string, url: string, headers: Array<HttpHeader>, };
|
||||
|
||||
export type CallHttpAuthenticationResponse = {
|
||||
/**
|
||||
* HTTP headers to add to the request. Existing headers will be replaced, while
|
||||
* new headers will be added.
|
||||
*/
|
||||
setHeaders: Array<HttpHeader>, };
|
||||
|
||||
export type CallHttpRequestActionArgs = { httpRequest: HttpRequest, };
|
||||
|
||||
export type CallHttpRequestActionRequest = { index: number, pluginRefId: string, args: CallHttpRequestActionArgs, };
|
||||
|
||||
export type CallTemplateFunctionArgs = { purpose: RenderPurpose, values: { [key in string]?: string }, };
|
||||
|
||||
export type CallTemplateFunctionRequest = { name: string, args: CallTemplateFunctionArgs, };
|
||||
|
||||
export type CallTemplateFunctionResponse = { value: string | null, };
|
||||
|
||||
export type CloseWindowRequest = { label: string, };
|
||||
|
||||
export type Color = "primary" | "secondary" | "info" | "success" | "notice" | "warning" | "danger";
|
||||
|
||||
export type CompletionOptionType = "constant" | "variable";
|
||||
|
||||
export type Content = { "type": "text", content: string, } | { "type": "markdown", content: string, };
|
||||
|
||||
export type CopyTextRequest = { text: string, };
|
||||
|
||||
export type DeleteKeyValueRequest = { key: string, };
|
||||
|
||||
export type DeleteKeyValueResponse = { deleted: boolean, };
|
||||
|
||||
export type EditorLanguage = "text" | "javascript" | "json" | "html" | "xml" | "graphql" | "markdown";
|
||||
|
||||
export type EmptyPayload = {};
|
||||
|
||||
export type ErrorResponse = { error: string, };
|
||||
|
||||
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, };
|
||||
|
||||
export type FindHttpResponsesRequest = { requestId: string, limit?: number, };
|
||||
|
||||
export type FindHttpResponsesResponse = { httpResponses: Array<HttpResponse>, };
|
||||
|
||||
export type FormInput = { "type": "text" } & FormInputText | { "type": "editor" } & FormInputEditor | { "type": "select" } & FormInputSelect | { "type": "checkbox" } & FormInputCheckbox | { "type": "file" } & FormInputFile | { "type": "http_request" } & FormInputHttpRequest | { "type": "accordion" } & FormInputAccordion | { "type": "banner" } & FormInputBanner | { "type": "markdown" } & FormInputMarkdown;
|
||||
|
||||
export type FormInputAccordion = { label: string, inputs?: Array<FormInput>, hidden?: boolean, };
|
||||
|
||||
export type FormInputBanner = { inputs?: Array<FormInput>, hidden?: boolean, color?: Color, };
|
||||
|
||||
export type FormInputBase = {
|
||||
/**
|
||||
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||
*/
|
||||
name: string,
|
||||
/**
|
||||
* Whether this input is visible for the given configuration. Use this to
|
||||
* make branching forms.
|
||||
*/
|
||||
hidden?: boolean,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
/**
|
||||
* Visually hide the label of the input
|
||||
*/
|
||||
hideLabel?: boolean,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, disabled?: boolean, };
|
||||
|
||||
export type FormInputCheckbox = {
|
||||
/**
|
||||
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||
*/
|
||||
name: string,
|
||||
/**
|
||||
* Whether this input is visible for the given configuration. Use this to
|
||||
* make branching forms.
|
||||
*/
|
||||
hidden?: boolean,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
/**
|
||||
* Visually hide the label of the input
|
||||
*/
|
||||
hideLabel?: boolean,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, disabled?: boolean, };
|
||||
|
||||
export type FormInputEditor = {
|
||||
/**
|
||||
* Placeholder for the text input
|
||||
*/
|
||||
placeholder?: string | null,
|
||||
/**
|
||||
* Don't show the editor gutter (line numbers, folds, etc.)
|
||||
*/
|
||||
hideGutter?: boolean,
|
||||
/**
|
||||
* Language for syntax highlighting
|
||||
*/
|
||||
language?: EditorLanguage, readOnly?: boolean, completionOptions?: Array<GenericCompletionOption>,
|
||||
/**
|
||||
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||
*/
|
||||
name: string,
|
||||
/**
|
||||
* Whether this input is visible for the given configuration. Use this to
|
||||
* make branching forms.
|
||||
*/
|
||||
hidden?: boolean,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
/**
|
||||
* Visually hide the label of the input
|
||||
*/
|
||||
hideLabel?: boolean,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, disabled?: boolean, };
|
||||
|
||||
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>,
|
||||
/**
|
||||
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||
*/
|
||||
name: string,
|
||||
/**
|
||||
* Whether this input is visible for the given configuration. Use this to
|
||||
* make branching forms.
|
||||
*/
|
||||
hidden?: boolean,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
/**
|
||||
* Visually hide the label of the input
|
||||
*/
|
||||
hideLabel?: boolean,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, disabled?: boolean, };
|
||||
|
||||
export type FormInputHttpRequest = {
|
||||
/**
|
||||
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||
*/
|
||||
name: string,
|
||||
/**
|
||||
* Whether this input is visible for the given configuration. Use this to
|
||||
* make branching forms.
|
||||
*/
|
||||
hidden?: boolean,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
/**
|
||||
* Visually hide the label of the input
|
||||
*/
|
||||
hideLabel?: boolean,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, disabled?: boolean, };
|
||||
|
||||
export type FormInputMarkdown = { content: string, hidden?: boolean, };
|
||||
|
||||
export type FormInputSelect = {
|
||||
/**
|
||||
* The options that will be available in the select input
|
||||
*/
|
||||
options: Array<FormInputSelectOption>,
|
||||
/**
|
||||
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||
*/
|
||||
name: string,
|
||||
/**
|
||||
* Whether this input is visible for the given configuration. Use this to
|
||||
* make branching forms.
|
||||
*/
|
||||
hidden?: boolean,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
/**
|
||||
* Visually hide the label of the input
|
||||
*/
|
||||
hideLabel?: boolean,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, disabled?: boolean, };
|
||||
|
||||
export type FormInputSelectOption = { label: string, value: string, };
|
||||
|
||||
export type FormInputText = {
|
||||
/**
|
||||
* Placeholder for the text input
|
||||
*/
|
||||
placeholder?: string | null,
|
||||
/**
|
||||
* Placeholder for the text input
|
||||
*/
|
||||
password?: boolean,
|
||||
/**
|
||||
* Whether to allow newlines in the input, like a <textarea/>
|
||||
*/
|
||||
multiLine?: boolean, completionOptions?: Array<GenericCompletionOption>,
|
||||
/**
|
||||
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||
*/
|
||||
name: string,
|
||||
/**
|
||||
* Whether this input is visible for the given configuration. Use this to
|
||||
* make branching forms.
|
||||
*/
|
||||
hidden?: boolean,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
/**
|
||||
* Visually hide the label of the input
|
||||
*/
|
||||
hideLabel?: boolean,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, disabled?: boolean, };
|
||||
|
||||
export type GenericCompletionOption = { label: string, detail?: string, info?: string, type?: CompletionOptionType, boost?: number, };
|
||||
|
||||
export type GetHttpAuthenticationConfigRequest = { contextId: string, values: { [key in string]?: JsonPrimitive }, };
|
||||
|
||||
export type GetHttpAuthenticationConfigResponse = { args: Array<FormInput>, pluginRefId: string, actions?: Array<HttpAuthenticationAction>, };
|
||||
|
||||
export type GetHttpAuthenticationSummaryResponse = { name: string, label: string, shortLabel: string, };
|
||||
|
||||
export type GetHttpRequestActionsRequest = Record<string, never>;
|
||||
|
||||
export type GetHttpRequestActionsResponse = { actions: Array<HttpRequestAction>, pluginRefId: string, };
|
||||
|
||||
export type GetHttpRequestByIdRequest = { id: string, };
|
||||
|
||||
export type GetHttpRequestByIdResponse = { httpRequest: HttpRequest | null, };
|
||||
|
||||
export type GetKeyValueRequest = { key: string, };
|
||||
|
||||
export type GetKeyValueResponse = { value?: string, };
|
||||
|
||||
export type GetTemplateFunctionsResponse = { functions: Array<TemplateFunction>, pluginRefId: string, };
|
||||
|
||||
export type HttpAuthenticationAction = { label: string, icon?: Icon, };
|
||||
|
||||
export type HttpHeader = { name: string, value: string, };
|
||||
|
||||
export type HttpRequestAction = { label: string, icon?: Icon, };
|
||||
|
||||
export type Icon = "alert_triangle" | "check" | "check_circle" | "chevron_down" | "copy" | "info" | "pin" | "search" | "trash" | "_unknown";
|
||||
|
||||
export type ImportRequest = { content: string, };
|
||||
|
||||
export type ImportResources = { workspaces: Array<Workspace>, environments: Array<Environment>, folders: Array<Folder>, httpRequests: Array<HttpRequest>, grpcRequests: Array<GrpcRequest>, };
|
||||
|
||||
export type ImportResponse = { resources: ImportResources, };
|
||||
|
||||
export type InternalEvent = { id: string, pluginRefId: string, pluginName: string, replyId: string | null, windowContext: WindowContext, payload: InternalEventPayload, };
|
||||
|
||||
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_summary_request" } & EmptyPayload | { "type": "get_http_authentication_summary_response" } & GetHttpAuthenticationSummaryResponse | { "type": "get_http_authentication_config_request" } & GetHttpAuthenticationConfigRequest | { "type": "get_http_authentication_config_response" } & GetHttpAuthenticationConfigResponse | { "type": "call_http_authentication_request" } & CallHttpAuthenticationRequest | { "type": "call_http_authentication_response" } & CallHttpAuthenticationResponse | { "type": "call_http_authentication_action_request" } & CallHttpAuthenticationActionRequest | { "type": "call_http_authentication_action_response" } & EmptyPayload | { "type": "copy_text_request" } & CopyTextRequest | { "type": "copy_text_response" } & EmptyPayload | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "get_key_value_request" } & GetKeyValueRequest | { "type": "get_key_value_response" } & GetKeyValueResponse | { "type": "set_key_value_request" } & SetKeyValueRequest | { "type": "set_key_value_response" } & SetKeyValueResponse | { "type": "delete_key_value_request" } & DeleteKeyValueRequest | { "type": "delete_key_value_response" } & DeleteKeyValueResponse | { "type": "open_window_request" } & OpenWindowRequest | { "type": "window_navigate_event" } & WindowNavigateEvent | { "type": "close_window_request" } & CloseWindowRequest | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "show_toast_request" } & ShowToastRequest | { "type": "show_toast_response" } & EmptyPayload | { "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 | { "type": "error_response" } & ErrorResponse;
|
||||
|
||||
export type JsonPrimitive = string | number | boolean | null;
|
||||
|
||||
export type OpenWindowRequest = { url: string,
|
||||
/**
|
||||
* Label for the window. If not provided, a random one will be generated.
|
||||
*/
|
||||
label: string, title?: string, size?: WindowSize, };
|
||||
|
||||
export type PromptTextRequest = { id: string, title: string, label: string, description?: string, defaultValue?: string, placeholder?: string,
|
||||
/**
|
||||
* Text to add to the confirmation button
|
||||
*/
|
||||
confirmText?: string,
|
||||
/**
|
||||
* Text to add to the cancel button
|
||||
*/
|
||||
cancelText?: string,
|
||||
/**
|
||||
* Require the user to enter a non-empty value
|
||||
*/
|
||||
required?: boolean, };
|
||||
|
||||
export type PromptTextResponse = { value: string | null, };
|
||||
|
||||
export type RenderHttpRequestRequest = { httpRequest: HttpRequest, purpose: RenderPurpose, };
|
||||
|
||||
export type RenderHttpRequestResponse = { httpRequest: HttpRequest, };
|
||||
|
||||
export type RenderPurpose = "send" | "preview";
|
||||
|
||||
export type SendHttpRequestRequest = { httpRequest: Partial<HttpRequest>, };
|
||||
|
||||
export type SendHttpRequestResponse = { httpResponse: HttpResponse, };
|
||||
|
||||
export type SetKeyValueRequest = { key: string, value: string, };
|
||||
|
||||
export type SetKeyValueResponse = {};
|
||||
|
||||
export type ShowToastRequest = { message: string, color?: Color, icon?: Icon, };
|
||||
|
||||
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<FormInput>, };
|
||||
|
||||
export type TemplateRenderRequest = { data: JsonValue, purpose: RenderPurpose, };
|
||||
|
||||
export type TemplateRenderResponse = { data: JsonValue, };
|
||||
|
||||
export type WindowContext = { "type": "none" } | { "type": "label", label: string, };
|
||||
|
||||
export type WindowNavigateEvent = { url: string, };
|
||||
|
||||
export type WindowSize = { width: number, height: number, };
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './bindings/models';
|
||||
export * from './bindings/events';
|
||||
export * from './bindings/gen_models';
|
||||
export * from './bindings/gen_events';
|
||||
|
||||
@@ -20,6 +20,9 @@ pub enum Error {
|
||||
#[error("JSON error: {0}")]
|
||||
JsonErr(#[from] serde_json::Error),
|
||||
|
||||
#[error("Timeout elapsed: {0}")]
|
||||
TimeoutElapsed(#[from] tokio::time::error::Elapsed),
|
||||
|
||||
#[error("Plugin not found: {0}")]
|
||||
PluginNotFoundErr(String),
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
use tauri::{Runtime, WebviewWindow};
|
||||
use ts_rs::TS;
|
||||
@@ -8,7 +7,7 @@ use yaak_models::models::{Environment, Folder, GrpcRequest, HttpRequest, HttpRes
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct InternalEvent {
|
||||
pub id: String,
|
||||
pub plugin_ref_id: String,
|
||||
@@ -29,12 +28,12 @@ pub(crate) struct InternalEventRawPayload {
|
||||
pub plugin_name: String,
|
||||
pub reply_id: Option<String>,
|
||||
pub window_context: WindowContext,
|
||||
pub payload: Value,
|
||||
pub payload: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "snake_case", tag = "type")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub enum WindowContext {
|
||||
None,
|
||||
Label { label: String },
|
||||
@@ -50,7 +49,7 @@ impl WindowContext {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "snake_case", tag = "type")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub enum InternalEventPayload {
|
||||
BootRequest(BootRequest),
|
||||
BootResponse(BootResponse),
|
||||
@@ -84,20 +83,38 @@ pub enum InternalEventPayload {
|
||||
CallTemplateFunctionRequest(CallTemplateFunctionRequest),
|
||||
CallTemplateFunctionResponse(CallTemplateFunctionResponse),
|
||||
|
||||
GetHttpAuthenticationRequest(EmptyPayload),
|
||||
GetHttpAuthenticationResponse(GetHttpAuthenticationResponse),
|
||||
// Http Authentication
|
||||
GetHttpAuthenticationSummaryRequest(EmptyPayload),
|
||||
GetHttpAuthenticationSummaryResponse(GetHttpAuthenticationSummaryResponse),
|
||||
GetHttpAuthenticationConfigRequest(GetHttpAuthenticationConfigRequest),
|
||||
GetHttpAuthenticationConfigResponse(GetHttpAuthenticationConfigResponse),
|
||||
CallHttpAuthenticationRequest(CallHttpAuthenticationRequest),
|
||||
CallHttpAuthenticationResponse(CallHttpAuthenticationResponse),
|
||||
CallHttpAuthenticationActionRequest(CallHttpAuthenticationActionRequest),
|
||||
CallHttpAuthenticationActionResponse(EmptyPayload),
|
||||
|
||||
CopyTextRequest(CopyTextRequest),
|
||||
CopyTextResponse(EmptyPayload),
|
||||
|
||||
RenderHttpRequestRequest(RenderHttpRequestRequest),
|
||||
RenderHttpRequestResponse(RenderHttpRequestResponse),
|
||||
|
||||
GetKeyValueRequest(GetKeyValueRequest),
|
||||
GetKeyValueResponse(GetKeyValueResponse),
|
||||
SetKeyValueRequest(SetKeyValueRequest),
|
||||
SetKeyValueResponse(SetKeyValueResponse),
|
||||
DeleteKeyValueRequest(DeleteKeyValueRequest),
|
||||
DeleteKeyValueResponse(DeleteKeyValueResponse),
|
||||
|
||||
OpenWindowRequest(OpenWindowRequest),
|
||||
WindowNavigateEvent(WindowNavigateEvent),
|
||||
CloseWindowRequest(CloseWindowRequest),
|
||||
|
||||
TemplateRenderRequest(TemplateRenderRequest),
|
||||
TemplateRenderResponse(TemplateRenderResponse),
|
||||
|
||||
ShowToastRequest(ShowToastRequest),
|
||||
ShowToastResponse(EmptyPayload),
|
||||
|
||||
PromptTextRequest(PromptTextRequest),
|
||||
PromptTextResponse(PromptTextResponse),
|
||||
@@ -117,19 +134,19 @@ pub enum InternalEventPayload {
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default)]
|
||||
#[ts(export, type = "{}", export_to = "events.ts")]
|
||||
#[ts(export, type = "{}", export_to = "gen_events.ts")]
|
||||
pub struct EmptyPayload {}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default)]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct ErrorResponse {
|
||||
pub error: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct BootRequest {
|
||||
pub dir: String,
|
||||
pub watch: bool,
|
||||
@@ -137,7 +154,7 @@ pub struct BootRequest {
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct BootResponse {
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
@@ -145,21 +162,21 @@ pub struct BootResponse {
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct ImportRequest {
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct ImportResponse {
|
||||
pub resources: ImportResources,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct FilterRequest {
|
||||
pub content: String,
|
||||
pub filter: String,
|
||||
@@ -167,49 +184,50 @@ pub struct FilterRequest {
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct FilterResponse {
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct ExportHttpRequestRequest {
|
||||
pub http_request: HttpRequest,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct ExportHttpRequestResponse {
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct SendHttpRequestRequest {
|
||||
#[ts(type = "Partial<HttpRequest>")]
|
||||
pub http_request: HttpRequest,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct SendHttpRequestResponse {
|
||||
pub http_response: HttpResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct CopyTextRequest {
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct RenderHttpRequestRequest {
|
||||
pub http_request: HttpRequest,
|
||||
pub purpose: RenderPurpose,
|
||||
@@ -217,14 +235,14 @@ pub struct RenderHttpRequestRequest {
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct RenderHttpRequestResponse {
|
||||
pub http_request: HttpRequest,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct TemplateRenderRequest {
|
||||
pub data: serde_json::Value,
|
||||
pub purpose: RenderPurpose,
|
||||
@@ -232,25 +250,62 @@ pub struct TemplateRenderRequest {
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct TemplateRenderResponse {
|
||||
pub data: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct OpenWindowRequest {
|
||||
pub url: String,
|
||||
/// Label for the window. If not provided, a random one will be generated.
|
||||
pub label: String,
|
||||
#[ts(optional)]
|
||||
pub title: Option<String>,
|
||||
#[ts(optional)]
|
||||
pub size: Option<WindowSize>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct WindowSize {
|
||||
pub width: f64,
|
||||
pub height: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct CloseWindowRequest {
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct WindowNavigateEvent {
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct ShowToastRequest {
|
||||
pub message: String,
|
||||
|
||||
#[ts(optional)]
|
||||
pub color: Option<Color>,
|
||||
|
||||
#[ts(optional)]
|
||||
pub icon: Option<Icon>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct PromptTextRequest {
|
||||
// A unique ID to identify the prompt (eg. "enter-password")
|
||||
pub id: String,
|
||||
@@ -277,17 +332,15 @@ pub struct PromptTextRequest {
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct PromptTextResponse {
|
||||
pub value: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub enum Color {
|
||||
Custom,
|
||||
Default,
|
||||
Primary,
|
||||
Secondary,
|
||||
Info,
|
||||
@@ -299,18 +352,23 @@ pub enum Color {
|
||||
|
||||
impl Default for Color {
|
||||
fn default() -> Self {
|
||||
Color::Default
|
||||
Color::Secondary
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub enum Icon {
|
||||
AlertTriangle,
|
||||
Check,
|
||||
CheckCircle,
|
||||
ChevronDown,
|
||||
Copy,
|
||||
Info,
|
||||
CheckCircle,
|
||||
AlertTriangle,
|
||||
Pin,
|
||||
Search,
|
||||
Trash,
|
||||
|
||||
#[serde(untagged)]
|
||||
#[ts(type = "\"_unknown\"")]
|
||||
@@ -319,17 +377,45 @@ pub enum Icon {
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
pub struct GetHttpAuthenticationResponse {
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct GetHttpAuthenticationSummaryResponse {
|
||||
pub name: String,
|
||||
pub label: String,
|
||||
pub short_label: String,
|
||||
pub config: Vec<FormInput>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct HttpAuthenticationAction {
|
||||
pub label: String,
|
||||
|
||||
#[ts(optional)]
|
||||
pub icon: Option<Icon>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct GetHttpAuthenticationConfigRequest {
|
||||
pub context_id: String,
|
||||
pub values: HashMap<String, JsonPrimitive>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct GetHttpAuthenticationConfigResponse {
|
||||
pub args: Vec<FormInput>,
|
||||
pub plugin_ref_id: String,
|
||||
|
||||
#[ts(optional)]
|
||||
pub actions: Option<Vec<HttpAuthenticationAction>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct HttpHeader {
|
||||
pub name: String,
|
||||
pub value: String,
|
||||
@@ -337,9 +423,10 @@ pub struct HttpHeader {
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct CallHttpAuthenticationRequest {
|
||||
pub config: serde_json::Map<String, serde_json::Value>,
|
||||
pub context_id: String,
|
||||
pub values: HashMap<String, JsonPrimitive>,
|
||||
pub method: String,
|
||||
pub url: String,
|
||||
pub headers: Vec<HttpHeader>,
|
||||
@@ -347,7 +434,34 @@ pub struct CallHttpAuthenticationRequest {
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct CallHttpAuthenticationActionRequest {
|
||||
pub index: i32,
|
||||
pub plugin_ref_id: String,
|
||||
pub args: CallHttpAuthenticationActionArgs,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct CallHttpAuthenticationActionArgs {
|
||||
pub context_id: String,
|
||||
pub values: HashMap<String, JsonPrimitive>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(untagged)]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub enum JsonPrimitive {
|
||||
String(String),
|
||||
Number(f64),
|
||||
Boolean(bool),
|
||||
Null,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct CallHttpAuthenticationResponse {
|
||||
/// HTTP headers to add to the request. Existing headers will be replaced, while
|
||||
/// new headers will be added.
|
||||
@@ -356,7 +470,7 @@ pub struct CallHttpAuthenticationResponse {
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct GetTemplateFunctionsResponse {
|
||||
pub functions: Vec<TemplateFunction>,
|
||||
pub plugin_ref_id: String,
|
||||
@@ -364,9 +478,10 @@ pub struct GetTemplateFunctionsResponse {
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct TemplateFunction {
|
||||
pub name: String,
|
||||
|
||||
#[ts(optional)]
|
||||
pub description: Option<String>,
|
||||
|
||||
@@ -379,7 +494,7 @@ pub struct TemplateFunction {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "snake_case", tag = "type")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub enum FormInput {
|
||||
Text(FormInputText),
|
||||
Editor(FormInputEditor),
|
||||
@@ -387,14 +502,23 @@ pub enum FormInput {
|
||||
Checkbox(FormInputCheckbox),
|
||||
File(FormInputFile),
|
||||
HttpRequest(FormInputHttpRequest),
|
||||
Accordion(FormInputAccordion),
|
||||
Banner(FormInputBanner),
|
||||
Markdown(FormInputMarkdown),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct FormInputBase {
|
||||
/// The name of the input. The value will be stored at this object attribute in the resulting data
|
||||
pub name: String,
|
||||
|
||||
/// Whether this input is visible for the given configuration. Use this to
|
||||
/// make branching forms.
|
||||
#[ts(optional)]
|
||||
pub hidden: Option<bool>,
|
||||
|
||||
/// Whether the user must fill in the argument
|
||||
#[ts(optional)]
|
||||
pub optional: Option<bool>,
|
||||
@@ -410,11 +534,14 @@ pub struct FormInputBase {
|
||||
/// The default value
|
||||
#[ts(optional)]
|
||||
pub default_value: Option<String>,
|
||||
|
||||
#[ts(optional)]
|
||||
pub disabled: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct FormInputText {
|
||||
#[serde(flatten)]
|
||||
pub base: FormInputBase,
|
||||
@@ -426,11 +553,18 @@ pub struct FormInputText {
|
||||
/// Placeholder for the text input
|
||||
#[ts(optional)]
|
||||
pub password: Option<bool>,
|
||||
|
||||
/// Whether to allow newlines in the input, like a <textarea/>
|
||||
#[ts(optional)]
|
||||
pub multi_line: Option<bool>,
|
||||
|
||||
#[ts(optional)]
|
||||
pub completion_options: Option<Vec<GenericCompletionOption>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub enum EditorLanguage {
|
||||
Text,
|
||||
Javascript,
|
||||
@@ -449,7 +583,7 @@ impl Default for EditorLanguage {
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct FormInputEditor {
|
||||
#[serde(flatten)]
|
||||
pub base: FormInputBase,
|
||||
@@ -465,11 +599,45 @@ pub struct FormInputEditor {
|
||||
/// Language for syntax highlighting
|
||||
#[ts(optional)]
|
||||
pub language: Option<EditorLanguage>,
|
||||
|
||||
#[ts(optional)]
|
||||
pub read_only: Option<bool>,
|
||||
|
||||
#[ts(optional)]
|
||||
pub completion_options: Option<Vec<GenericCompletionOption>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct GenericCompletionOption {
|
||||
label: String,
|
||||
|
||||
#[ts(optional)]
|
||||
detail: Option<String>,
|
||||
|
||||
#[ts(optional)]
|
||||
info: Option<String>,
|
||||
|
||||
#[ts(optional)]
|
||||
#[serde(rename = "type")]
|
||||
pub type_: Option<CompletionOptionType>,
|
||||
|
||||
#[ts(optional)]
|
||||
pub boost: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub enum CompletionOptionType {
|
||||
Constant,
|
||||
Variable,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct FormInputHttpRequest {
|
||||
#[serde(flatten)]
|
||||
pub base: FormInputBase,
|
||||
@@ -477,7 +645,7 @@ pub struct FormInputHttpRequest {
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct FormInputFile {
|
||||
#[serde(flatten)]
|
||||
pub base: FormInputBase,
|
||||
@@ -504,7 +672,7 @@ pub struct FormInputFile {
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct FileFilter {
|
||||
pub name: String,
|
||||
/// File extensions to require
|
||||
@@ -513,7 +681,7 @@ pub struct FileFilter {
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct FormInputSelect {
|
||||
#[serde(flatten)]
|
||||
pub base: FormInputBase,
|
||||
@@ -524,7 +692,7 @@ pub struct FormInputSelect {
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct FormInputCheckbox {
|
||||
#[serde(flatten)]
|
||||
pub base: FormInputBase,
|
||||
@@ -532,15 +700,68 @@ pub struct FormInputCheckbox {
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct FormInputSelectOption {
|
||||
pub name: String,
|
||||
pub label: String,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct FormInputAccordion {
|
||||
pub label: String,
|
||||
|
||||
#[ts(optional)]
|
||||
pub inputs: Option<Vec<FormInput>>,
|
||||
|
||||
#[ts(optional)]
|
||||
pub hidden: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct FormInputBanner {
|
||||
#[ts(optional)]
|
||||
pub inputs: Option<Vec<FormInput>>,
|
||||
|
||||
#[ts(optional)]
|
||||
pub hidden: Option<bool>,
|
||||
|
||||
#[ts(optional)]
|
||||
pub color: Option<Color>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct FormInputMarkdown {
|
||||
pub content: String,
|
||||
|
||||
#[ts(optional)]
|
||||
pub hidden: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "snake_case", tag = "type")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub enum Content {
|
||||
Text { content: String },
|
||||
Markdown { content: String },
|
||||
}
|
||||
|
||||
impl Default for Content {
|
||||
fn default() -> Self {
|
||||
Self::Text {
|
||||
content: String::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct CallTemplateFunctionRequest {
|
||||
pub name: String,
|
||||
pub args: CallTemplateFunctionArgs,
|
||||
@@ -548,14 +769,14 @@ pub struct CallTemplateFunctionRequest {
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct CallTemplateFunctionResponse {
|
||||
pub value: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct CallTemplateFunctionArgs {
|
||||
pub purpose: RenderPurpose,
|
||||
pub values: HashMap<String, String>,
|
||||
@@ -563,7 +784,7 @@ pub struct CallTemplateFunctionArgs {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub enum RenderPurpose {
|
||||
Send,
|
||||
Preview,
|
||||
@@ -577,12 +798,12 @@ impl Default for RenderPurpose {
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default)]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct GetHttpRequestActionsRequest {}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct GetHttpRequestActionsResponse {
|
||||
pub actions: Vec<HttpRequestAction>,
|
||||
pub plugin_ref_id: String,
|
||||
@@ -590,9 +811,8 @@ pub struct GetHttpRequestActionsResponse {
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct HttpRequestAction {
|
||||
pub key: String,
|
||||
pub label: String,
|
||||
#[ts(optional)]
|
||||
pub icon: Option<Icon>,
|
||||
@@ -600,37 +820,37 @@ pub struct HttpRequestAction {
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct CallHttpRequestActionRequest {
|
||||
pub key: String,
|
||||
pub index: i32,
|
||||
pub plugin_ref_id: String,
|
||||
pub args: CallHttpRequestActionArgs,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct CallHttpRequestActionArgs {
|
||||
pub http_request: HttpRequest,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct GetHttpRequestByIdRequest {
|
||||
pub id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct GetHttpRequestByIdResponse {
|
||||
pub http_request: Option<HttpRequest>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct FindHttpResponsesRequest {
|
||||
pub request_id: String,
|
||||
#[ts(optional)]
|
||||
@@ -639,14 +859,14 @@ pub struct FindHttpResponsesRequest {
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct FindHttpResponsesResponse {
|
||||
pub http_responses: Vec<HttpResponse>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct ImportResources {
|
||||
pub workspaces: Vec<Workspace>,
|
||||
pub environments: Vec<Environment>,
|
||||
@@ -654,3 +874,45 @@ pub struct ImportResources {
|
||||
pub http_requests: Vec<HttpRequest>,
|
||||
pub grpc_requests: Vec<GrpcRequest>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct GetKeyValueRequest {
|
||||
pub key: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct GetKeyValueResponse {
|
||||
#[ts(optional)]
|
||||
pub value: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct SetKeyValueRequest {
|
||||
pub key: String,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default)]
|
||||
#[ts(export, type = "{}", export_to = "gen_events.ts")]
|
||||
pub struct SetKeyValueResponse {}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct DeleteKeyValueRequest {
|
||||
pub key: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default)]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct DeleteKeyValueResponse {
|
||||
pub deleted: bool,
|
||||
}
|
||||
|
||||
@@ -3,12 +3,13 @@ use crate::error::Error::{
|
||||
};
|
||||
use crate::error::Result;
|
||||
use crate::events::{
|
||||
BootRequest, CallHttpAuthenticationRequest, CallHttpAuthenticationResponse,
|
||||
CallHttpRequestActionRequest, CallTemplateFunctionArgs, CallTemplateFunctionRequest,
|
||||
CallTemplateFunctionResponse, EmptyPayload, FilterRequest, FilterResponse, FormInput,
|
||||
GetHttpAuthenticationResponse, GetHttpRequestActionsResponse, GetTemplateFunctionsResponse,
|
||||
ImportRequest, ImportResponse, InternalEvent, InternalEventPayload, RenderPurpose,
|
||||
WindowContext,
|
||||
BootRequest, CallHttpAuthenticationActionArgs, CallHttpAuthenticationActionRequest,
|
||||
CallHttpAuthenticationRequest, CallHttpAuthenticationResponse, CallHttpRequestActionRequest,
|
||||
CallTemplateFunctionArgs, CallTemplateFunctionRequest, CallTemplateFunctionResponse,
|
||||
EmptyPayload, FilterRequest, FilterResponse, GetHttpAuthenticationConfigRequest,
|
||||
GetHttpAuthenticationConfigResponse, GetHttpAuthenticationSummaryResponse,
|
||||
GetHttpRequestActionsResponse, GetTemplateFunctionsResponse, ImportRequest, ImportResponse,
|
||||
InternalEvent, InternalEventPayload, JsonPrimitive, RenderPurpose, WindowContext,
|
||||
};
|
||||
use crate::nodejs::start_nodejs_plugin_runtime;
|
||||
use crate::plugin_handle::PluginHandle;
|
||||
@@ -24,6 +25,7 @@ use tauri::{AppHandle, Manager, Runtime, WebviewWindow};
|
||||
use tokio::fs::read_dir;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::sync::{mpsc, Mutex};
|
||||
use tokio::time::timeout;
|
||||
use yaak_models::queries::{generate_id, list_plugins};
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -86,7 +88,7 @@ impl PluginManager {
|
||||
let addr = listener.local_addr().expect("Failed to get local address");
|
||||
|
||||
// 1. Reload all plugins when the Node.js runtime connects
|
||||
{
|
||||
let init_plugins_task = {
|
||||
let plugin_manager = plugin_manager.clone();
|
||||
let app_handle = app_handle.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
@@ -94,7 +96,7 @@ impl PluginManager {
|
||||
Ok(_) => {
|
||||
info!("Plugin runtime client connected!");
|
||||
plugin_manager
|
||||
.initialize_all_plugins(&app_handle, WindowContext::None)
|
||||
.initialize_all_plugins(&app_handle, &WindowContext::None)
|
||||
.await
|
||||
.expect("Failed to reload plugins");
|
||||
}
|
||||
@@ -102,7 +104,7 @@ impl PluginManager {
|
||||
warn!("Failed to receive from client connection rx {e:?}");
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
};
|
||||
|
||||
// 1. Spawn server in the background
|
||||
@@ -114,8 +116,13 @@ impl PluginManager {
|
||||
// 2. Start Node.js runtime and initialize plugins
|
||||
tauri::async_runtime::block_on(async move {
|
||||
start_nodejs_plugin_runtime(&app_handle, addr, &kill_server_rx).await.unwrap();
|
||||
info!("Waiting for plugins to initialize");
|
||||
init_plugins_task.await.unwrap();
|
||||
});
|
||||
|
||||
// 3. Block waiting for plugins to initialize
|
||||
tauri::async_runtime::block_on(async move {});
|
||||
|
||||
plugin_manager
|
||||
}
|
||||
|
||||
@@ -160,14 +167,14 @@ impl PluginManager {
|
||||
[bundled_plugin_dirs, installed_plugin_dirs].concat()
|
||||
}
|
||||
|
||||
pub async fn uninstall(&self, window_context: WindowContext, dir: &str) -> Result<()> {
|
||||
pub async fn uninstall(&self, window_context: &WindowContext, dir: &str) -> Result<()> {
|
||||
let plugin = self.get_plugin_by_dir(dir).await.ok_or(PluginNotFoundErr(dir.to_string()))?;
|
||||
self.remove_plugin(window_context, &plugin).await
|
||||
}
|
||||
|
||||
async fn remove_plugin(
|
||||
&self,
|
||||
window_context: WindowContext,
|
||||
window_context: &WindowContext,
|
||||
plugin: &PluginHandle,
|
||||
) -> Result<()> {
|
||||
// Terminate the plugin
|
||||
@@ -185,7 +192,7 @@ impl PluginManager {
|
||||
|
||||
pub async fn add_plugin_by_dir(
|
||||
&self,
|
||||
window_context: WindowContext,
|
||||
window_context: &WindowContext,
|
||||
dir: &str,
|
||||
watch: bool,
|
||||
) -> Result<()> {
|
||||
@@ -197,20 +204,22 @@ impl PluginManager {
|
||||
};
|
||||
let plugin_handle = PluginHandle::new(dir, tx.clone());
|
||||
|
||||
// Add the new plugin
|
||||
self.plugins.lock().await.push(plugin_handle.clone());
|
||||
|
||||
// Boot the plugin
|
||||
let event = self
|
||||
.send_to_plugin_and_wait(
|
||||
let event = timeout(
|
||||
Duration::from_secs(1),
|
||||
self.send_to_plugin_and_wait(
|
||||
window_context,
|
||||
&plugin_handle,
|
||||
&InternalEventPayload::BootRequest(BootRequest {
|
||||
dir: dir.to_string(),
|
||||
watch,
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
),
|
||||
)
|
||||
.await??;
|
||||
|
||||
// Add the new plugin
|
||||
self.plugins.lock().await.push(plugin_handle.clone());
|
||||
|
||||
let resp = match event.payload {
|
||||
InternalEventPayload::BootResponse(resp) => resp,
|
||||
@@ -226,20 +235,21 @@ impl PluginManager {
|
||||
pub async fn initialize_all_plugins<R: Runtime>(
|
||||
&self,
|
||||
app_handle: &AppHandle<R>,
|
||||
window_context: WindowContext,
|
||||
window_context: &WindowContext,
|
||||
) -> Result<()> {
|
||||
let dirs = self.list_plugin_dirs(app_handle).await;
|
||||
for d in dirs.clone() {
|
||||
let candidates = self.list_plugin_dirs(app_handle).await;
|
||||
for candidate in candidates.clone() {
|
||||
// First remove the plugin if it exists
|
||||
if let Some(plugin) = self.get_plugin_by_dir(d.dir.as_str()).await {
|
||||
if let Err(e) = self.remove_plugin(window_context.to_owned(), &plugin).await {
|
||||
warn!("Failed to remove plugin {} {e:?}", d.dir);
|
||||
if let Some(plugin) = self.get_plugin_by_dir(candidate.dir.as_str()).await {
|
||||
if let Err(e) = self.remove_plugin(window_context, &plugin).await {
|
||||
warn!("Failed to remove plugin {} {e:?}", candidate.dir);
|
||||
}
|
||||
}
|
||||
if let Err(e) =
|
||||
self.add_plugin_by_dir(window_context.to_owned(), d.dir.as_str(), d.watch).await
|
||||
if let Err(e) = self
|
||||
.add_plugin_by_dir(window_context, candidate.dir.as_str(), candidate.watch)
|
||||
.await
|
||||
{
|
||||
warn!("Failed to add plugin {} {e:?}", d.dir);
|
||||
warn!("Failed to add plugin {} {e:?}", candidate.dir);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,13 +290,13 @@ impl PluginManager {
|
||||
source_event: &InternalEvent,
|
||||
payload: &InternalEventPayload,
|
||||
) -> Result<()> {
|
||||
let window_label = source_event.to_owned().window_context;
|
||||
let window_context = source_event.to_owned().window_context;
|
||||
let reply_id = Some(source_event.to_owned().id);
|
||||
let plugin = self
|
||||
.get_plugin_by_ref_id(source_event.plugin_ref_id.as_str())
|
||||
.await
|
||||
.ok_or(PluginNotFoundErr(source_event.plugin_ref_id.to_string()))?;
|
||||
let event = plugin.build_event_to_send_raw(window_label, &payload, reply_id);
|
||||
let event = plugin.build_event_to_send_raw(&window_context, &payload, reply_id);
|
||||
plugin.send(&event).await
|
||||
}
|
||||
|
||||
@@ -310,7 +320,7 @@ impl PluginManager {
|
||||
|
||||
async fn send_to_plugin_and_wait(
|
||||
&self,
|
||||
window_context: WindowContext,
|
||||
window_context: &WindowContext,
|
||||
plugin: &PluginHandle,
|
||||
payload: &InternalEventPayload,
|
||||
) -> Result<InternalEvent> {
|
||||
@@ -321,7 +331,7 @@ impl PluginManager {
|
||||
|
||||
async fn send_and_wait(
|
||||
&self,
|
||||
window_context: WindowContext,
|
||||
window_context: &WindowContext,
|
||||
payload: &InternalEventPayload,
|
||||
) -> Result<Vec<InternalEvent>> {
|
||||
let plugins = { self.plugins.lock().await.clone() };
|
||||
@@ -330,7 +340,7 @@ impl PluginManager {
|
||||
|
||||
async fn send_to_plugins_and_wait(
|
||||
&self,
|
||||
window_context: WindowContext,
|
||||
window_context: &WindowContext,
|
||||
payload: &InternalEventPayload,
|
||||
plugins: Vec<PluginHandle>,
|
||||
) -> Result<Vec<InternalEvent>> {
|
||||
@@ -340,7 +350,7 @@ impl PluginManager {
|
||||
// 1. Build the events with IDs and everything
|
||||
let events_to_send = plugins
|
||||
.iter()
|
||||
.map(|p| p.build_event_to_send(window_context.to_owned(), payload, None))
|
||||
.map(|p| p.build_event_to_send(window_context, payload, None))
|
||||
.collect::<Vec<InternalEvent>>();
|
||||
|
||||
// 2. Spawn thread to subscribe to incoming events and check reply ids
|
||||
@@ -358,9 +368,9 @@ impl PluginManager {
|
||||
if matched_sent_event {
|
||||
found_events.push(event.clone());
|
||||
};
|
||||
|
||||
|
||||
let found_them_all = found_events.len() == events_to_send.len();
|
||||
if found_them_all{
|
||||
if found_them_all {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -393,7 +403,7 @@ impl PluginManager {
|
||||
) -> Result<Vec<GetHttpRequestActionsResponse>> {
|
||||
let reply_events = self
|
||||
.send_and_wait(
|
||||
WindowContext::from_window(window),
|
||||
&WindowContext::from_window(window),
|
||||
&InternalEventPayload::GetHttpRequestActionsRequest(EmptyPayload {}),
|
||||
)
|
||||
.await?;
|
||||
@@ -412,12 +422,12 @@ impl PluginManager {
|
||||
&self,
|
||||
window: &WebviewWindow<R>,
|
||||
) -> Result<Vec<GetTemplateFunctionsResponse>> {
|
||||
self.get_template_functions_with_context(WindowContext::from_window(window)).await
|
||||
self.get_template_functions_with_context(&WindowContext::from_window(window)).await
|
||||
}
|
||||
|
||||
pub async fn get_template_functions_with_context(
|
||||
&self,
|
||||
window_context: WindowContext,
|
||||
window_context: &WindowContext,
|
||||
) -> Result<Vec<GetTemplateFunctionsResponse>> {
|
||||
let reply_events = self
|
||||
.send_and_wait(window_context, &InternalEventPayload::GetTemplateFunctionsRequest)
|
||||
@@ -442,7 +452,7 @@ impl PluginManager {
|
||||
let plugin =
|
||||
self.get_plugin_by_ref_id(ref_id.as_str()).await.ok_or(PluginNotFoundErr(ref_id))?;
|
||||
let event = plugin.build_event_to_send(
|
||||
WindowContext::from_window(window),
|
||||
&WindowContext::from_window(window),
|
||||
&InternalEventPayload::CallHttpRequestActionRequest(req),
|
||||
None,
|
||||
);
|
||||
@@ -450,21 +460,22 @@ impl PluginManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_http_authentication<R: Runtime>(
|
||||
pub async fn get_http_authentication_summaries<R: Runtime>(
|
||||
&self,
|
||||
window: &WebviewWindow<R>,
|
||||
) -> Result<Vec<(PluginHandle, GetHttpAuthenticationResponse)>> {
|
||||
) -> Result<Vec<(PluginHandle, GetHttpAuthenticationSummaryResponse)>> {
|
||||
let window_context = WindowContext::from_window(window);
|
||||
let reply_events = self
|
||||
.send_and_wait(
|
||||
window_context,
|
||||
&InternalEventPayload::GetHttpAuthenticationRequest(EmptyPayload {}),
|
||||
&window_context,
|
||||
&InternalEventPayload::GetHttpAuthenticationSummaryRequest(EmptyPayload {}),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut results = Vec::new();
|
||||
for event in reply_events {
|
||||
if let InternalEventPayload::GetHttpAuthenticationResponse(resp) = event.payload {
|
||||
if let InternalEventPayload::GetHttpAuthenticationSummaryResponse(resp) = event.payload
|
||||
{
|
||||
let plugin = self
|
||||
.get_plugin_by_ref_id(&event.plugin_ref_id)
|
||||
.await
|
||||
@@ -476,43 +487,84 @@ impl PluginManager {
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
pub async fn get_http_authentication_config<R: Runtime>(
|
||||
&self,
|
||||
window: &WebviewWindow<R>,
|
||||
auth_name: &str,
|
||||
values: HashMap<String, JsonPrimitive>,
|
||||
request_id: &str,
|
||||
) -> Result<GetHttpAuthenticationConfigResponse> {
|
||||
let results = self.get_http_authentication_summaries(window).await?;
|
||||
let plugin = results
|
||||
.iter()
|
||||
.find_map(|(p, r)| if r.name == auth_name { Some(p) } else { None })
|
||||
.ok_or(PluginNotFoundErr(auth_name.into()))?;
|
||||
|
||||
let context_id = format!("{:x}", md5::compute(request_id.to_string()));
|
||||
let event = self
|
||||
.send_to_plugin_and_wait(
|
||||
&WindowContext::from_window(window),
|
||||
&plugin,
|
||||
&InternalEventPayload::GetHttpAuthenticationConfigRequest(
|
||||
GetHttpAuthenticationConfigRequest { values, context_id },
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
match event.payload {
|
||||
InternalEventPayload::GetHttpAuthenticationConfigResponse(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_http_authentication_action<R: Runtime>(
|
||||
&self,
|
||||
window: &WebviewWindow<R>,
|
||||
auth_name: &str,
|
||||
action_index: i32,
|
||||
values: HashMap<String, JsonPrimitive>,
|
||||
request_id: &str,
|
||||
) -> Result<()> {
|
||||
let results = self.get_http_authentication_summaries(window).await?;
|
||||
let plugin = results
|
||||
.iter()
|
||||
.find_map(|(p, r)| if r.name == auth_name { Some(p) } else { None })
|
||||
.ok_or(PluginNotFoundErr(auth_name.into()))?;
|
||||
|
||||
let context_id = format!("{:x}", md5::compute(request_id.to_string()));
|
||||
self
|
||||
.send_to_plugin_and_wait(
|
||||
&WindowContext::from_window(window),
|
||||
&plugin,
|
||||
&InternalEventPayload::CallHttpAuthenticationActionRequest(
|
||||
CallHttpAuthenticationActionRequest {
|
||||
index: action_index,
|
||||
plugin_ref_id: plugin.clone().ref_id,
|
||||
args: CallHttpAuthenticationActionArgs { context_id, values },
|
||||
},
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn call_http_authentication<R: Runtime>(
|
||||
&self,
|
||||
window: &WebviewWindow<R>,
|
||||
auth_name: &str,
|
||||
req: CallHttpAuthenticationRequest,
|
||||
) -> Result<CallHttpAuthenticationResponse> {
|
||||
let handlers = self.get_http_authentication(window).await?;
|
||||
let (plugin, authentication) = handlers
|
||||
let handlers = self.get_http_authentication_summaries(window).await?;
|
||||
let (plugin, _) = handlers
|
||||
.iter()
|
||||
.find(|(_, a)| a.name == auth_name)
|
||||
.ok_or(AuthPluginNotFound(auth_name.to_string()))?;
|
||||
|
||||
// Clone for mutability
|
||||
let mut req = req.clone();
|
||||
|
||||
// Fill in default values
|
||||
for arg in authentication.config.clone() {
|
||||
let base = match arg {
|
||||
FormInput::Text(a) => a.base,
|
||||
FormInput::Editor(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 = req.config.get(base.name.as_str()) {
|
||||
let default = match base.default_value {
|
||||
None => serde_json::Value::Null,
|
||||
Some(s) => serde_json::Value::String(s),
|
||||
};
|
||||
req.config.insert(base.name, default);
|
||||
}
|
||||
}
|
||||
|
||||
let event = self
|
||||
.send_to_plugin_and_wait(
|
||||
WindowContext::from_window(window),
|
||||
&WindowContext::from_window(window),
|
||||
&plugin,
|
||||
&InternalEventPayload::CallHttpAuthenticationRequest(req),
|
||||
)
|
||||
@@ -528,7 +580,7 @@ impl PluginManager {
|
||||
|
||||
pub async fn call_template_function(
|
||||
&self,
|
||||
window_context: WindowContext,
|
||||
window_context: &WindowContext,
|
||||
fn_name: &str,
|
||||
args: HashMap<String, String>,
|
||||
purpose: RenderPurpose,
|
||||
@@ -562,7 +614,7 @@ impl PluginManager {
|
||||
) -> Result<(ImportResponse, String)> {
|
||||
let reply_events = self
|
||||
.send_and_wait(
|
||||
WindowContext::from_window(window),
|
||||
&WindowContext::from_window(window),
|
||||
&InternalEventPayload::ImportRequest(ImportRequest {
|
||||
content: content.to_string(),
|
||||
}),
|
||||
@@ -607,7 +659,7 @@ impl PluginManager {
|
||||
|
||||
let event = self
|
||||
.send_to_plugin_and_wait(
|
||||
WindowContext::from_window(window),
|
||||
&WindowContext::from_window(window),
|
||||
&plugin,
|
||||
&InternalEventPayload::FilterRequest(FilterRequest {
|
||||
filter: filter.to_string(),
|
||||
|
||||
@@ -26,6 +26,10 @@ impl PluginHandle {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn name(&self) -> String {
|
||||
self.boot_resp.lock().await.name.clone()
|
||||
}
|
||||
|
||||
pub async fn info(&self) -> BootResponse {
|
||||
let resp = &*self.boot_resp.lock().await;
|
||||
resp.clone()
|
||||
@@ -33,7 +37,7 @@ impl PluginHandle {
|
||||
|
||||
pub fn build_event_to_send(
|
||||
&self,
|
||||
window_context: WindowContext,
|
||||
window_context: &WindowContext,
|
||||
payload: &InternalEventPayload,
|
||||
reply_id: Option<String>,
|
||||
) -> InternalEvent {
|
||||
@@ -42,7 +46,7 @@ impl PluginHandle {
|
||||
|
||||
pub(crate) fn build_event_to_send_raw(
|
||||
&self,
|
||||
window_context: WindowContext,
|
||||
window_context: &WindowContext,
|
||||
payload: &InternalEventPayload,
|
||||
reply_id: Option<String>,
|
||||
) -> InternalEvent {
|
||||
@@ -53,18 +57,18 @@ impl PluginHandle {
|
||||
plugin_name: dir.file_name().unwrap().to_str().unwrap().to_string(),
|
||||
reply_id,
|
||||
payload: payload.clone(),
|
||||
window_context,
|
||||
window_context: window_context.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn terminate(&self, window_context: WindowContext) -> Result<()> {
|
||||
pub async fn terminate(&self, window_context: &WindowContext) -> Result<()> {
|
||||
info!("Terminating plugin {}", self.dir);
|
||||
let event =
|
||||
self.build_event_to_send(window_context, &InternalEventPayload::TerminateRequest, None);
|
||||
self.send(&event).await
|
||||
}
|
||||
|
||||
pub(crate) async fn send(&self, event: &InternalEvent) -> Result<()> {
|
||||
pub async fn send(&self, event: &InternalEvent) -> Result<()> {
|
||||
self.to_plugin_tx.lock().await.send(event.to_owned()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -85,8 +85,9 @@ impl PluginRuntimeServerWebsocket {
|
||||
};
|
||||
|
||||
// Parse everything but the payload so we can catch errors on that, specifically
|
||||
let payload = serde_json::from_value::<InternalEventPayload>(event.payload)
|
||||
let payload = serde_json::from_value::<InternalEventPayload>(event.payload.clone())
|
||||
.unwrap_or_else(|e| {
|
||||
warn!("Plugin error from {}: {:?} {}", event.plugin_name, e, event.payload);
|
||||
InternalEventPayload::ErrorResponse(ErrorResponse {
|
||||
error: format!("Plugin error from {}: {e:?}", event.plugin_name),
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { SyncModel } from "./models.js";
|
||||
import type { SyncState } from "./models.js";
|
||||
import type { SyncModel } from "./gen_models.js";
|
||||
import type { SyncState } from "./gen_models.js";
|
||||
|
||||
export type FsCandidate = { "type": "FsCandidate", model: SyncModel, relPath: string, checksum: string, };
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Channel, invoke } from '@tauri-apps/api/core';
|
||||
import { emit } from '@tauri-apps/api/event';
|
||||
import { SyncOp } from './bindings/sync';
|
||||
import { WatchEvent, WatchResult } from './bindings/watch';
|
||||
import { SyncOp } from './bindings/gen_sync';
|
||||
import { WatchEvent, WatchResult } from './bindings/gen_watch';
|
||||
|
||||
export async function calculateSync(workspaceId: string, syncDir: string) {
|
||||
return invoke<SyncOp[]>('plugin:yaak-sync|calculate', {
|
||||
|
||||
@@ -51,7 +51,7 @@ pub async fn apply<R: Runtime>(
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "watch.ts")]
|
||||
#[ts(export, export_to = "gen_watch.ts")]
|
||||
pub(crate) struct WatchResult {
|
||||
unlisten_event: String,
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use yaak_models::models::{AnyModel, Environment, Folder, GrpcRequest, HttpReques
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "snake_case", tag = "type")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub enum SyncModel {
|
||||
Workspace(Workspace),
|
||||
Environment(Environment),
|
||||
|
||||
@@ -21,7 +21,7 @@ use yaak_models::queries::{
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "camelCase", tag = "type")]
|
||||
#[ts(export, export_to = "sync.ts")]
|
||||
#[ts(export, export_to = "gen_sync.ts")]
|
||||
pub(crate) enum SyncOp {
|
||||
FsCreate {
|
||||
model: SyncModel,
|
||||
@@ -98,7 +98,7 @@ impl DbCandidate {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "camelCase", tag = "type")]
|
||||
#[ts(export, export_to = "sync.ts")]
|
||||
#[ts(export, export_to = "gen_sync.ts")]
|
||||
pub(crate) struct FsCandidate {
|
||||
pub(crate) model: SyncModel,
|
||||
pub(crate) rel_path: PathBuf,
|
||||
|
||||
@@ -10,7 +10,7 @@ use ts_rs::TS;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "watch.ts")]
|
||||
#[ts(export, export_to = "gen_watch.ts")]
|
||||
pub(crate) struct WatchEvent {
|
||||
paths: Vec<PathBuf>,
|
||||
kind: String,
|
||||
|
||||
Reference in New Issue
Block a user