mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-31 06:23:08 +02:00
Websocket Support (#159)
This commit is contained in:
@@ -3,3 +3,4 @@ pub mod models;
|
||||
pub mod queries;
|
||||
|
||||
pub mod plugin;
|
||||
pub mod render;
|
||||
|
||||
@@ -125,7 +125,7 @@ impl<'s> TryFrom<&Row<'s>> for Settings {
|
||||
fn try_from(r: &Row<'s>) -> Result<Self, Self::Error> {
|
||||
let proxy: Option<String> = r.get("proxy")?;
|
||||
let editor_keymap: String = r.get("editor_keymap")?;
|
||||
Ok(Settings {
|
||||
Ok(Self {
|
||||
id: r.get("id")?,
|
||||
model: r.get("model")?,
|
||||
created_at: r.get("created_at")?,
|
||||
@@ -187,7 +187,7 @@ impl<'s> TryFrom<&Row<'s>> for Workspace {
|
||||
type Error = rusqlite::Error;
|
||||
|
||||
fn try_from(r: &Row<'s>) -> Result<Self, Self::Error> {
|
||||
Ok(Workspace {
|
||||
Ok(Self {
|
||||
id: r.get("id")?,
|
||||
model: r.get("model")?,
|
||||
created_at: r.get("created_at")?,
|
||||
@@ -243,7 +243,7 @@ impl<'s> TryFrom<&Row<'s>> for WorkspaceMeta {
|
||||
type Error = rusqlite::Error;
|
||||
|
||||
fn try_from(r: &Row<'s>) -> Result<Self, Self::Error> {
|
||||
Ok(WorkspaceMeta {
|
||||
Ok(Self {
|
||||
id: r.get("id")?,
|
||||
workspace_id: r.get("workspace_id")?,
|
||||
model: r.get("model")?,
|
||||
@@ -313,7 +313,7 @@ impl<'s> TryFrom<&Row<'s>> for CookieJar {
|
||||
|
||||
fn try_from(r: &Row<'s>) -> Result<Self, Self::Error> {
|
||||
let cookies: String = r.get("cookies")?;
|
||||
Ok(CookieJar {
|
||||
Ok(Self {
|
||||
id: r.get("id")?,
|
||||
model: r.get("model")?,
|
||||
workspace_id: r.get("workspace_id")?,
|
||||
@@ -361,7 +361,7 @@ impl<'s> TryFrom<&Row<'s>> for Environment {
|
||||
|
||||
fn try_from(r: &Row<'s>) -> Result<Self, Self::Error> {
|
||||
let variables: String = r.get("variables")?;
|
||||
Ok(Environment {
|
||||
Ok(Self {
|
||||
id: r.get("id")?,
|
||||
model: r.get("model")?,
|
||||
workspace_id: r.get("workspace_id")?,
|
||||
@@ -424,7 +424,7 @@ impl<'s> TryFrom<&Row<'s>> for Folder {
|
||||
type Error = rusqlite::Error;
|
||||
|
||||
fn try_from(r: &Row<'s>) -> Result<Self, Self::Error> {
|
||||
Ok(Folder {
|
||||
Ok(Self {
|
||||
id: r.get("id")?,
|
||||
model: r.get("model")?,
|
||||
sort_priority: r.get("sort_priority")?,
|
||||
@@ -484,7 +484,7 @@ pub struct HttpRequest {
|
||||
pub body_type: Option<String>,
|
||||
pub description: String,
|
||||
pub headers: Vec<HttpRequestHeader>,
|
||||
#[serde(default = "default_http_request_method")]
|
||||
#[serde(default = "default_http_method")]
|
||||
pub method: String,
|
||||
pub name: String,
|
||||
pub sort_priority: f32,
|
||||
@@ -524,7 +524,7 @@ impl<'s> TryFrom<&Row<'s>> for HttpRequest {
|
||||
let body: String = r.get("body")?;
|
||||
let authentication: String = r.get("authentication")?;
|
||||
let headers: String = r.get("headers")?;
|
||||
Ok(HttpRequest {
|
||||
Ok(Self {
|
||||
id: r.get("id")?,
|
||||
model: r.get("model")?,
|
||||
sort_priority: r.get("sort_priority")?,
|
||||
@@ -546,6 +546,243 @@ impl<'s> TryFrom<&Row<'s>> for HttpRequest {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub enum WebsocketConnectionState {
|
||||
Initialized,
|
||||
Connected,
|
||||
Closed,
|
||||
}
|
||||
|
||||
impl Default for WebsocketConnectionState {
|
||||
fn default() -> Self {
|
||||
Self::Initialized
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct WebsocketConnection {
|
||||
#[ts(type = "\"websocket_connection\"")]
|
||||
pub model: String,
|
||||
pub id: String,
|
||||
pub created_at: NaiveDateTime,
|
||||
pub updated_at: NaiveDateTime,
|
||||
pub workspace_id: String,
|
||||
pub request_id: String,
|
||||
|
||||
pub elapsed: i32,
|
||||
pub error: Option<String>,
|
||||
pub headers: Vec<HttpResponseHeader>,
|
||||
pub state: WebsocketConnectionState,
|
||||
pub status: i32,
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
#[derive(Iden)]
|
||||
pub enum WebsocketConnectionIden {
|
||||
#[iden = "websocket_connections"]
|
||||
Table,
|
||||
Id,
|
||||
Model,
|
||||
CreatedAt,
|
||||
UpdatedAt,
|
||||
WorkspaceId,
|
||||
RequestId,
|
||||
|
||||
Elapsed,
|
||||
Error,
|
||||
Headers,
|
||||
State,
|
||||
Status,
|
||||
Url,
|
||||
}
|
||||
|
||||
impl<'s> TryFrom<&Row<'s>> for WebsocketConnection {
|
||||
type Error = rusqlite::Error;
|
||||
|
||||
fn try_from(r: &Row<'s>) -> Result<Self, Self::Error> {
|
||||
let headers: String = r.get("headers")?;
|
||||
let state: String = r.get("state")?;
|
||||
Ok(Self {
|
||||
id: r.get("id")?,
|
||||
model: r.get("model")?,
|
||||
workspace_id: r.get("workspace_id")?,
|
||||
request_id: r.get("request_id")?,
|
||||
created_at: r.get("created_at")?,
|
||||
updated_at: r.get("updated_at")?,
|
||||
url: r.get("url")?,
|
||||
headers: serde_json::from_str(headers.as_str()).unwrap_or_default(),
|
||||
elapsed: r.get("elapsed")?,
|
||||
error: r.get("error")?,
|
||||
state: serde_json::from_str(format!(r#""{state}""#).as_str()).unwrap(),
|
||||
status: r.get("status")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub enum WebsocketMessageType {
|
||||
Text,
|
||||
Binary,
|
||||
}
|
||||
|
||||
impl Default for WebsocketMessageType {
|
||||
fn default() -> Self {
|
||||
Self::Text
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct WebsocketRequest {
|
||||
#[ts(type = "\"websocket_request\"")]
|
||||
pub model: String,
|
||||
pub id: String,
|
||||
pub created_at: NaiveDateTime,
|
||||
pub updated_at: NaiveDateTime,
|
||||
pub workspace_id: String,
|
||||
pub folder_id: Option<String>,
|
||||
|
||||
#[ts(type = "Record<string, any>")]
|
||||
pub authentication: BTreeMap<String, Value>,
|
||||
pub authentication_type: Option<String>,
|
||||
pub description: String,
|
||||
pub headers: Vec<HttpRequestHeader>,
|
||||
pub message: String,
|
||||
pub name: String,
|
||||
pub sort_priority: f32,
|
||||
pub url: String,
|
||||
pub url_parameters: Vec<HttpUrlParameter>,
|
||||
}
|
||||
|
||||
#[derive(Iden)]
|
||||
pub enum WebsocketRequestIden {
|
||||
#[iden = "websocket_requests"]
|
||||
Table,
|
||||
Id,
|
||||
Model,
|
||||
CreatedAt,
|
||||
UpdatedAt,
|
||||
WorkspaceId,
|
||||
FolderId,
|
||||
|
||||
Authentication,
|
||||
AuthenticationType,
|
||||
Message,
|
||||
Description,
|
||||
Headers,
|
||||
Name,
|
||||
SortPriority,
|
||||
Url,
|
||||
UrlParameters,
|
||||
}
|
||||
|
||||
impl<'s> TryFrom<&Row<'s>> for WebsocketRequest {
|
||||
type Error = rusqlite::Error;
|
||||
|
||||
fn try_from(r: &Row<'s>) -> Result<Self, Self::Error> {
|
||||
let url_parameters: String = r.get("url_parameters")?;
|
||||
let authentication: String = r.get("authentication")?;
|
||||
let headers: String = r.get("headers")?;
|
||||
Ok(Self {
|
||||
id: r.get("id")?,
|
||||
model: r.get("model")?,
|
||||
sort_priority: r.get("sort_priority")?,
|
||||
workspace_id: r.get("workspace_id")?,
|
||||
created_at: r.get("created_at")?,
|
||||
updated_at: r.get("updated_at")?,
|
||||
url: r.get("url")?,
|
||||
url_parameters: serde_json::from_str(url_parameters.as_str()).unwrap_or_default(),
|
||||
message: r.get("message")?,
|
||||
description: r.get("description")?,
|
||||
authentication: serde_json::from_str(authentication.as_str()).unwrap_or_default(),
|
||||
authentication_type: r.get("authentication_type")?,
|
||||
headers: serde_json::from_str(headers.as_str()).unwrap_or_default(),
|
||||
folder_id: r.get("folder_id")?,
|
||||
name: r.get("name")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub enum WebsocketEventType {
|
||||
Binary,
|
||||
Close,
|
||||
Frame,
|
||||
Ping,
|
||||
Pong,
|
||||
Text,
|
||||
}
|
||||
|
||||
impl Default for WebsocketEventType {
|
||||
fn default() -> Self {
|
||||
Self::Text
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct WebsocketEvent {
|
||||
#[ts(type = "\"websocket_event\"")]
|
||||
pub model: String,
|
||||
pub id: String,
|
||||
pub created_at: NaiveDateTime,
|
||||
pub updated_at: NaiveDateTime,
|
||||
pub workspace_id: String,
|
||||
pub request_id: String,
|
||||
pub connection_id: String,
|
||||
pub is_server: bool,
|
||||
|
||||
pub message: Vec<u8>,
|
||||
pub message_type: WebsocketEventType,
|
||||
}
|
||||
|
||||
#[derive(Iden)]
|
||||
pub enum WebsocketEventIden {
|
||||
#[iden = "websocket_events"]
|
||||
Table,
|
||||
Model,
|
||||
Id,
|
||||
CreatedAt,
|
||||
UpdatedAt,
|
||||
WorkspaceId,
|
||||
RequestId,
|
||||
ConnectionId,
|
||||
IsServer,
|
||||
|
||||
MessageType,
|
||||
Message,
|
||||
}
|
||||
|
||||
impl<'s> TryFrom<&Row<'s>> for WebsocketEvent {
|
||||
type Error = rusqlite::Error;
|
||||
|
||||
fn try_from(r: &Row<'s>) -> Result<Self, Self::Error> {
|
||||
let message_type: String = r.get("message_type")?;
|
||||
Ok(Self {
|
||||
id: r.get("id")?,
|
||||
model: r.get("model")?,
|
||||
workspace_id: r.get("workspace_id")?,
|
||||
request_id: r.get("request_id")?,
|
||||
connection_id: r.get("connection_id")?,
|
||||
created_at: r.get("created_at")?,
|
||||
updated_at: r.get("updated_at")?,
|
||||
message: r.get("message")?,
|
||||
is_server: r.get("is_server")?,
|
||||
message_type: serde_json::from_str(message_type.as_str()).unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
@@ -626,7 +863,7 @@ impl<'s> TryFrom<&Row<'s>> for HttpResponse {
|
||||
fn try_from(r: &Row<'s>) -> Result<Self, Self::Error> {
|
||||
let headers: String = r.get("headers")?;
|
||||
let state: String = r.get("state")?;
|
||||
Ok(HttpResponse {
|
||||
Ok(Self {
|
||||
id: r.get("id")?,
|
||||
model: r.get("model")?,
|
||||
workspace_id: r.get("workspace_id")?,
|
||||
@@ -725,7 +962,7 @@ impl<'s> TryFrom<&Row<'s>> for GrpcRequest {
|
||||
fn try_from(r: &Row<'s>) -> Result<Self, Self::Error> {
|
||||
let authentication: String = r.get("authentication")?;
|
||||
let metadata: String = r.get("metadata")?;
|
||||
Ok(GrpcRequest {
|
||||
Ok(Self {
|
||||
id: r.get("id")?,
|
||||
model: r.get("model")?,
|
||||
workspace_id: r.get("workspace_id")?,
|
||||
@@ -810,7 +1047,7 @@ impl<'s> TryFrom<&Row<'s>> for GrpcConnection {
|
||||
fn try_from(r: &Row<'s>) -> Result<Self, Self::Error> {
|
||||
let trailers: String = r.get("trailers")?;
|
||||
let state: String = r.get("state")?;
|
||||
Ok(GrpcConnection {
|
||||
Ok(Self {
|
||||
id: r.get("id")?,
|
||||
model: r.get("model")?,
|
||||
workspace_id: r.get("workspace_id")?,
|
||||
@@ -892,7 +1129,7 @@ impl<'s> TryFrom<&Row<'s>> for GrpcEvent {
|
||||
fn try_from(r: &Row<'s>) -> Result<Self, Self::Error> {
|
||||
let event_type: String = r.get("event_type")?;
|
||||
let metadata: String = r.get("metadata")?;
|
||||
Ok(GrpcEvent {
|
||||
Ok(Self {
|
||||
id: r.get("id")?,
|
||||
model: r.get("model")?,
|
||||
workspace_id: r.get("workspace_id")?,
|
||||
@@ -944,7 +1181,7 @@ impl<'s> TryFrom<&Row<'s>> for Plugin {
|
||||
type Error = rusqlite::Error;
|
||||
|
||||
fn try_from(r: &Row<'s>) -> Result<Self, Self::Error> {
|
||||
Ok(Plugin {
|
||||
Ok(Self {
|
||||
id: r.get("id")?,
|
||||
model: r.get("model")?,
|
||||
created_at: r.get("created_at")?,
|
||||
@@ -1012,7 +1249,7 @@ impl<'s> TryFrom<&Row<'s>> for SyncState {
|
||||
type Error = rusqlite::Error;
|
||||
|
||||
fn try_from(r: &Row<'s>) -> Result<Self, Self::Error> {
|
||||
Ok(SyncState {
|
||||
Ok(Self {
|
||||
id: r.get("id")?,
|
||||
workspace_id: r.get("workspace_id")?,
|
||||
model: r.get("model")?,
|
||||
@@ -1058,7 +1295,7 @@ impl<'s> TryFrom<&Row<'s>> for KeyValue {
|
||||
type Error = rusqlite::Error;
|
||||
|
||||
fn try_from(r: &Row<'s>) -> Result<Self, Self::Error> {
|
||||
Ok(KeyValue {
|
||||
Ok(Self {
|
||||
model: r.get("model")?,
|
||||
created_at: r.get("created_at")?,
|
||||
updated_at: r.get("updated_at")?,
|
||||
@@ -1100,7 +1337,7 @@ impl<'s> TryFrom<&Row<'s>> for PluginKeyValue {
|
||||
type Error = rusqlite::Error;
|
||||
|
||||
fn try_from(r: &Row<'s>) -> Result<Self, Self::Error> {
|
||||
Ok(PluginKeyValue {
|
||||
Ok(Self {
|
||||
model: r.get("model")?,
|
||||
created_at: r.get("created_at")?,
|
||||
updated_at: r.get("updated_at")?,
|
||||
@@ -1115,7 +1352,7 @@ fn default_true() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn default_http_request_method() -> String {
|
||||
fn default_http_method() -> String {
|
||||
"GET".to_string()
|
||||
}
|
||||
|
||||
@@ -1129,9 +1366,12 @@ pub enum ModelType {
|
||||
TypeHttpRequest,
|
||||
TypeHttpResponse,
|
||||
TypePlugin,
|
||||
TypeSyncState,
|
||||
TypeWebSocketConnection,
|
||||
TypeWebSocketEvent,
|
||||
TypeWebsocketRequest,
|
||||
TypeWorkspace,
|
||||
TypeWorkspaceMeta,
|
||||
TypeSyncState,
|
||||
}
|
||||
|
||||
impl ModelType {
|
||||
@@ -1149,6 +1389,9 @@ impl ModelType {
|
||||
ModelType::TypeWorkspace => "wk",
|
||||
ModelType::TypeWorkspaceMeta => "wm",
|
||||
ModelType::TypeSyncState => "ss",
|
||||
ModelType::TypeWebSocketConnection => "wc",
|
||||
ModelType::TypeWebSocketEvent => "we",
|
||||
ModelType::TypeWebsocketRequest => "wr",
|
||||
}
|
||||
.to_string()
|
||||
}
|
||||
@@ -1171,6 +1414,9 @@ pub enum AnyModel {
|
||||
KeyValue(KeyValue),
|
||||
Workspace(Workspace),
|
||||
WorkspaceMeta(WorkspaceMeta),
|
||||
WebsocketConnection(WebsocketConnection),
|
||||
WebsocketEvent(WebsocketEvent),
|
||||
WebsocketRequest(WebsocketRequest),
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for AnyModel {
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
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, PluginKeyValue, PluginKeyValueIden, 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,
|
||||
WebsocketConnection, WebsocketConnectionIden, WebsocketEvent, WebsocketEventIden,
|
||||
WebsocketRequest, WebsocketRequestIden, Workspace, WorkspaceIden, WorkspaceMeta,
|
||||
WorkspaceMetaIden,
|
||||
};
|
||||
use crate::plugin::SqliteConnection;
|
||||
use chrono::{NaiveDateTime, Utc};
|
||||
use log::{debug, error, info, warn};
|
||||
@@ -16,8 +25,7 @@ use std::path::Path;
|
||||
use tauri::{AppHandle, Emitter, Listener, Manager, Runtime, WebviewWindow};
|
||||
use ts_rs::TS;
|
||||
|
||||
const MAX_GRPC_CONNECTIONS_PER_REQUEST: usize = 20;
|
||||
const MAX_HTTP_RESPONSES_PER_REQUEST: usize = MAX_GRPC_CONNECTIONS_PER_REQUEST;
|
||||
const MAX_HISTORY_ITEMS: usize = 20;
|
||||
|
||||
pub async fn set_key_value_string<R: Runtime>(
|
||||
mgr: &WebviewWindow<R>,
|
||||
@@ -659,8 +667,8 @@ pub async fn upsert_grpc_connection<R: Runtime>(
|
||||
update_source: &UpdateSource,
|
||||
) -> Result<GrpcConnection> {
|
||||
let connections =
|
||||
list_http_responses_for_request(window, connection.request_id.as_str(), None).await?;
|
||||
for c in connections.iter().skip(MAX_GRPC_CONNECTIONS_PER_REQUEST - 1) {
|
||||
list_grpc_connections_for_request(window, connection.request_id.as_str()).await?;
|
||||
for c in connections.iter().skip(MAX_HISTORY_ITEMS - 1) {
|
||||
debug!("Deleting old grpc connection {}", c.id);
|
||||
delete_grpc_connection(window, c.id.as_str(), update_source).await?;
|
||||
}
|
||||
@@ -911,6 +919,367 @@ pub async fn list_grpc_events<R: Runtime>(
|
||||
Ok(items.map(|v| v.unwrap()).collect())
|
||||
}
|
||||
|
||||
pub async fn delete_websocket_request<R: Runtime>(
|
||||
window: &WebviewWindow<R>,
|
||||
id: &str,
|
||||
update_source: &UpdateSource,
|
||||
) -> Result<WebsocketRequest> {
|
||||
let request = match get_websocket_request(window, id).await? {
|
||||
Some(r) => r,
|
||||
None => {
|
||||
return Err(ModelNotFound(id.to_string()));
|
||||
}
|
||||
};
|
||||
|
||||
let dbm = &*window.app_handle().state::<SqliteConnection>();
|
||||
let db = dbm.0.lock().await.get().unwrap();
|
||||
let (sql, params) = Query::delete()
|
||||
.from_table(WebsocketRequestIden::Table)
|
||||
.cond_where(Expr::col(WebsocketRequestIden::Id).eq(id))
|
||||
.build_rusqlite(SqliteQueryBuilder);
|
||||
db.execute(sql.as_str(), &*params.as_params())?;
|
||||
|
||||
emit_deleted_model(window, &AnyModel::WebsocketRequest(request.to_owned()), update_source);
|
||||
Ok(request)
|
||||
}
|
||||
|
||||
pub async fn delete_websocket_connection<R: Runtime>(
|
||||
window: &WebviewWindow<R>,
|
||||
id: &str,
|
||||
update_source: &UpdateSource,
|
||||
) -> Result<WebsocketConnection> {
|
||||
let m = get_websocket_connection(window, id).await?;
|
||||
|
||||
let dbm = &*window.app_handle().state::<SqliteConnection>();
|
||||
let db = dbm.0.lock().await.get().unwrap();
|
||||
let (sql, params) = Query::delete()
|
||||
.from_table(WebsocketConnectionIden::Table)
|
||||
.cond_where(Expr::col(WebsocketConnectionIden::Id).eq(id))
|
||||
.build_rusqlite(SqliteQueryBuilder);
|
||||
db.execute(sql.as_str(), &*params.as_params())?;
|
||||
|
||||
emit_deleted_model(window, &AnyModel::WebsocketConnection(m.to_owned()), update_source);
|
||||
Ok(m)
|
||||
}
|
||||
|
||||
pub async fn delete_all_websocket_connections<R: Runtime>(
|
||||
window: &WebviewWindow<R>,
|
||||
request_id: &str,
|
||||
update_source: &UpdateSource,
|
||||
) -> Result<()> {
|
||||
for c in list_websocket_connections_for_request(window, request_id).await? {
|
||||
delete_websocket_connection(window, &c.id, update_source).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete_all_websocket_connections_for_workspace<R: Runtime>(
|
||||
window: &WebviewWindow<R>,
|
||||
workspace_id: &str,
|
||||
update_source: &UpdateSource,
|
||||
) -> Result<()> {
|
||||
for c in list_websocket_connections_for_workspace(window, workspace_id).await? {
|
||||
delete_websocket_connection(window, &c.id, update_source).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_websocket_connection<R: Runtime>(
|
||||
mgr: &impl Manager<R>,
|
||||
id: &str,
|
||||
) -> Result<WebsocketConnection> {
|
||||
let dbm = &*mgr.state::<SqliteConnection>();
|
||||
let db = dbm.0.lock().await.get().unwrap();
|
||||
let (sql, params) = Query::select()
|
||||
.from(WebsocketConnectionIden::Table)
|
||||
.column(Asterisk)
|
||||
.cond_where(Expr::col(WebsocketConnectionIden::Id).eq(id))
|
||||
.build_rusqlite(SqliteQueryBuilder);
|
||||
let mut stmt = db.prepare(sql.as_str())?;
|
||||
Ok(stmt.query_row(&*params.as_params(), |row| row.try_into())?)
|
||||
}
|
||||
|
||||
pub async fn upsert_websocket_event<R: Runtime>(
|
||||
window: &WebviewWindow<R>,
|
||||
event: WebsocketEvent,
|
||||
update_source: &UpdateSource,
|
||||
) -> Result<WebsocketEvent> {
|
||||
let id = match event.id.as_str() {
|
||||
"" => generate_model_id(ModelType::TypeWebSocketEvent),
|
||||
_ => event.id.to_string(),
|
||||
};
|
||||
|
||||
let dbm = &*window.app_handle().state::<SqliteConnection>();
|
||||
let db = dbm.0.lock().await.get().unwrap();
|
||||
let (sql, params) = Query::insert()
|
||||
.into_table(WebsocketEventIden::Table)
|
||||
.columns([
|
||||
WebsocketEventIden::Id,
|
||||
WebsocketEventIden::CreatedAt,
|
||||
WebsocketEventIden::UpdatedAt,
|
||||
WebsocketEventIden::WorkspaceId,
|
||||
WebsocketEventIden::ConnectionId,
|
||||
WebsocketEventIden::RequestId,
|
||||
WebsocketEventIden::MessageType,
|
||||
WebsocketEventIden::IsServer,
|
||||
WebsocketEventIden::Message,
|
||||
])
|
||||
.values_panic([
|
||||
id.into(),
|
||||
timestamp_for_upsert(update_source, event.created_at).into(),
|
||||
timestamp_for_upsert(update_source, event.updated_at).into(),
|
||||
event.workspace_id.into(),
|
||||
event.connection_id.into(),
|
||||
event.request_id.into(),
|
||||
serde_json::to_string(&event.message_type)?.into(),
|
||||
event.is_server.into(),
|
||||
event.message.into(),
|
||||
])
|
||||
.on_conflict(
|
||||
OnConflict::column(WebsocketEventIden::Id)
|
||||
.update_columns([
|
||||
WebsocketEventIden::UpdatedAt,
|
||||
WebsocketEventIden::MessageType,
|
||||
WebsocketEventIden::IsServer,
|
||||
WebsocketEventIden::Message,
|
||||
])
|
||||
.to_owned(),
|
||||
)
|
||||
.returning_all()
|
||||
.build_rusqlite(SqliteQueryBuilder);
|
||||
|
||||
let mut stmt = db.prepare(sql.as_str())?;
|
||||
let m: WebsocketEvent = stmt.query_row(&*params.as_params(), |row| row.try_into())?;
|
||||
emit_upserted_model(window, &AnyModel::WebsocketEvent(m.to_owned()), update_source);
|
||||
Ok(m)
|
||||
}
|
||||
|
||||
pub async fn upsert_websocket_request<R: Runtime>(
|
||||
window: &WebviewWindow<R>,
|
||||
request: WebsocketRequest,
|
||||
update_source: &UpdateSource,
|
||||
) -> Result<WebsocketRequest> {
|
||||
let id = match request.id.as_str() {
|
||||
"" => generate_model_id(ModelType::TypeWebsocketRequest),
|
||||
_ => request.id.to_string(),
|
||||
};
|
||||
let trimmed_name = request.name.trim();
|
||||
|
||||
let dbm = &*window.app_handle().state::<SqliteConnection>();
|
||||
let db = dbm.0.lock().await.get().unwrap();
|
||||
let (sql, params) = Query::insert()
|
||||
.into_table(WebsocketRequestIden::Table)
|
||||
.columns([
|
||||
WebsocketRequestIden::Id,
|
||||
WebsocketRequestIden::CreatedAt,
|
||||
WebsocketRequestIden::UpdatedAt,
|
||||
WebsocketRequestIden::WorkspaceId,
|
||||
WebsocketRequestIden::FolderId,
|
||||
WebsocketRequestIden::Authentication,
|
||||
WebsocketRequestIden::AuthenticationType,
|
||||
WebsocketRequestIden::Description,
|
||||
WebsocketRequestIden::Headers,
|
||||
WebsocketRequestIden::Message,
|
||||
WebsocketRequestIden::Name,
|
||||
WebsocketRequestIden::SortPriority,
|
||||
WebsocketRequestIden::Url,
|
||||
WebsocketRequestIden::UrlParameters,
|
||||
])
|
||||
.values_panic([
|
||||
id.into(),
|
||||
timestamp_for_upsert(update_source, request.created_at).into(),
|
||||
timestamp_for_upsert(update_source, request.updated_at).into(),
|
||||
request.workspace_id.into(),
|
||||
request.folder_id.as_ref().map(|s| s.as_str()).into(),
|
||||
serde_json::to_string(&request.authentication)?.into(),
|
||||
request.authentication_type.as_ref().map(|s| s.as_str()).into(),
|
||||
request.description.into(),
|
||||
serde_json::to_string(&request.headers)?.into(),
|
||||
request.message.into(),
|
||||
trimmed_name.into(),
|
||||
request.sort_priority.into(),
|
||||
request.url.into(),
|
||||
serde_json::to_string(&request.url_parameters)?.into(),
|
||||
])
|
||||
.on_conflict(
|
||||
OnConflict::column(WebsocketRequestIden::Id)
|
||||
.update_columns([
|
||||
WebsocketRequestIden::UpdatedAt,
|
||||
WebsocketRequestIden::WorkspaceId,
|
||||
WebsocketRequestIden::FolderId,
|
||||
WebsocketRequestIden::Authentication,
|
||||
WebsocketRequestIden::AuthenticationType,
|
||||
WebsocketRequestIden::Description,
|
||||
WebsocketRequestIden::Headers,
|
||||
WebsocketRequestIden::Message,
|
||||
WebsocketRequestIden::Name,
|
||||
WebsocketRequestIden::SortPriority,
|
||||
WebsocketRequestIden::Url,
|
||||
WebsocketRequestIden::UrlParameters,
|
||||
])
|
||||
.to_owned(),
|
||||
)
|
||||
.returning_all()
|
||||
.build_rusqlite(SqliteQueryBuilder);
|
||||
|
||||
let mut stmt = db.prepare(sql.as_str())?;
|
||||
let m: WebsocketRequest = stmt.query_row(&*params.as_params(), |row| row.try_into())?;
|
||||
emit_upserted_model(window, &AnyModel::WebsocketRequest(m.to_owned()), update_source);
|
||||
Ok(m)
|
||||
}
|
||||
|
||||
pub async fn list_websocket_connections_for_workspace<R: Runtime>(
|
||||
mgr: &impl Manager<R>,
|
||||
workspace_id: &str,
|
||||
) -> Result<Vec<WebsocketConnection>> {
|
||||
let dbm = &*mgr.state::<SqliteConnection>();
|
||||
let db = dbm.0.lock().await.get().unwrap();
|
||||
|
||||
let (sql, params) = Query::select()
|
||||
.from(WebsocketConnectionIden::Table)
|
||||
.cond_where(Expr::col(WebsocketConnectionIden::WorkspaceId).eq(workspace_id))
|
||||
.column(Asterisk)
|
||||
.order_by(WebsocketConnectionIden::CreatedAt, Order::Desc)
|
||||
.build_rusqlite(SqliteQueryBuilder);
|
||||
let mut stmt = db.prepare(sql.as_str())?;
|
||||
let items = stmt.query_map(&*params.as_params(), |row| row.try_into())?;
|
||||
Ok(items.map(|v| v.unwrap()).collect())
|
||||
}
|
||||
|
||||
pub async fn list_websocket_connections_for_request<R: Runtime>(
|
||||
mgr: &impl Manager<R>,
|
||||
request_id: &str,
|
||||
) -> Result<Vec<WebsocketConnection>> {
|
||||
let dbm = &*mgr.state::<SqliteConnection>();
|
||||
let db = dbm.0.lock().await.get().unwrap();
|
||||
|
||||
let (sql, params) = Query::select()
|
||||
.from(WebsocketConnectionIden::Table)
|
||||
.cond_where(Expr::col(WebsocketConnectionIden::RequestId).eq(request_id))
|
||||
.column(Asterisk)
|
||||
.order_by(WebsocketConnectionIden::CreatedAt, Order::Desc)
|
||||
.build_rusqlite(SqliteQueryBuilder);
|
||||
let mut stmt = db.prepare(sql.as_str())?;
|
||||
let items = stmt.query_map(&*params.as_params(), |row| row.try_into())?;
|
||||
Ok(items.map(|v| v.unwrap()).collect())
|
||||
}
|
||||
|
||||
pub async fn upsert_websocket_connection<R: Runtime>(
|
||||
window: &WebviewWindow<R>,
|
||||
connection: &WebsocketConnection,
|
||||
update_source: &UpdateSource,
|
||||
) -> Result<WebsocketConnection> {
|
||||
let connections =
|
||||
list_websocket_connections_for_request(window, connection.request_id.as_str()).await?;
|
||||
for c in connections.iter().skip(MAX_HISTORY_ITEMS - 1) {
|
||||
debug!("Deleting old websocket connection {}", c.id);
|
||||
delete_websocket_connection(window, c.id.as_str(), update_source).await?;
|
||||
}
|
||||
|
||||
let id = match connection.id.as_str() {
|
||||
"" => generate_model_id(ModelType::TypeWebSocketConnection),
|
||||
_ => connection.id.to_string(),
|
||||
};
|
||||
let dbm = &*window.app_handle().state::<SqliteConnection>();
|
||||
let db = dbm.0.lock().await.get().unwrap();
|
||||
let (sql, params) = Query::insert()
|
||||
.into_table(WebsocketConnectionIden::Table)
|
||||
.columns([
|
||||
WebsocketConnectionIden::Id,
|
||||
WebsocketConnectionIden::CreatedAt,
|
||||
WebsocketConnectionIden::UpdatedAt,
|
||||
WebsocketConnectionIden::WorkspaceId,
|
||||
WebsocketConnectionIden::RequestId,
|
||||
WebsocketConnectionIden::Elapsed,
|
||||
WebsocketConnectionIden::Error,
|
||||
WebsocketConnectionIden::Headers,
|
||||
WebsocketConnectionIden::State,
|
||||
WebsocketConnectionIden::Status,
|
||||
WebsocketConnectionIden::Url,
|
||||
])
|
||||
.values_panic([
|
||||
id.as_str().into(),
|
||||
timestamp_for_upsert(update_source, connection.created_at).into(),
|
||||
timestamp_for_upsert(update_source, connection.updated_at).into(),
|
||||
connection.workspace_id.as_str().into(),
|
||||
connection.request_id.as_str().into(),
|
||||
connection.elapsed.into(),
|
||||
connection.error.as_ref().map(|s| s.as_str()).into(),
|
||||
serde_json::to_string(&connection.headers)?.into(),
|
||||
serde_json::to_value(&connection.state)?.as_str().into(),
|
||||
connection.status.into(),
|
||||
connection.url.as_str().into(),
|
||||
])
|
||||
.on_conflict(
|
||||
OnConflict::column(WebsocketConnectionIden::Id)
|
||||
.update_columns([
|
||||
WebsocketConnectionIden::UpdatedAt,
|
||||
WebsocketConnectionIden::Elapsed,
|
||||
WebsocketConnectionIden::Error,
|
||||
WebsocketConnectionIden::Headers,
|
||||
WebsocketConnectionIden::State,
|
||||
WebsocketConnectionIden::Status,
|
||||
WebsocketConnectionIden::Url,
|
||||
])
|
||||
.to_owned(),
|
||||
)
|
||||
.returning_all()
|
||||
.build_rusqlite(SqliteQueryBuilder);
|
||||
|
||||
let mut stmt = db.prepare(sql.as_str())?;
|
||||
let m: WebsocketConnection = stmt.query_row(&*params.as_params(), |row| row.try_into())?;
|
||||
emit_upserted_model(window, &AnyModel::WebsocketConnection(m.to_owned()), update_source);
|
||||
Ok(m)
|
||||
}
|
||||
|
||||
pub async fn get_websocket_request<R: Runtime>(
|
||||
mgr: &impl Manager<R>,
|
||||
id: &str,
|
||||
) -> Result<Option<WebsocketRequest>> {
|
||||
let dbm = &*mgr.state::<SqliteConnection>();
|
||||
let db = dbm.0.lock().await.get().unwrap();
|
||||
|
||||
let (sql, params) = Query::select()
|
||||
.from(WebsocketRequestIden::Table)
|
||||
.column(Asterisk)
|
||||
.cond_where(Expr::col(WebsocketRequestIden::Id).eq(id))
|
||||
.build_rusqlite(SqliteQueryBuilder);
|
||||
let mut stmt = db.prepare(sql.as_str())?;
|
||||
Ok(stmt.query_row(&*params.as_params(), |row| row.try_into()).optional()?)
|
||||
}
|
||||
|
||||
pub async fn list_websocket_requests<R: Runtime>(
|
||||
mgr: &impl Manager<R>,
|
||||
workspace_id: &str,
|
||||
) -> Result<Vec<WebsocketRequest>> {
|
||||
let dbm = &*mgr.state::<SqliteConnection>();
|
||||
let db = dbm.0.lock().await.get().unwrap();
|
||||
let (sql, params) = Query::select()
|
||||
.from(WebsocketRequestIden::Table)
|
||||
.cond_where(Expr::col(WebsocketRequestIden::WorkspaceId).eq(workspace_id))
|
||||
.column(Asterisk)
|
||||
.build_rusqlite(SqliteQueryBuilder);
|
||||
let mut stmt = db.prepare(sql.as_str())?;
|
||||
let items = stmt.query_map(&*params.as_params(), |row| row.try_into())?;
|
||||
Ok(items.map(|v| v.unwrap()).collect())
|
||||
}
|
||||
|
||||
pub async fn list_websocket_events<R: Runtime>(
|
||||
mgr: &impl Manager<R>,
|
||||
connection_id: &str,
|
||||
) -> Result<Vec<WebsocketEvent>> {
|
||||
let dbm = &*mgr.state::<SqliteConnection>();
|
||||
let db = dbm.0.lock().await.get().unwrap();
|
||||
let (sql, params) = Query::select()
|
||||
.from(WebsocketEventIden::Table)
|
||||
.cond_where(Expr::col(WebsocketEventIden::ConnectionId).eq(connection_id))
|
||||
.column(Asterisk)
|
||||
.build_rusqlite(SqliteQueryBuilder);
|
||||
let mut stmt = db.prepare(sql.as_str())?;
|
||||
let items = stmt.query_map(&*params.as_params(), |row| row.try_into())?;
|
||||
Ok(items.map(|v| v.unwrap()).collect())
|
||||
}
|
||||
|
||||
pub async fn upsert_cookie_jar<R: Runtime>(
|
||||
window: &WebviewWindow<R>,
|
||||
cookie_jar: &CookieJar,
|
||||
@@ -1676,7 +2045,7 @@ pub async fn create_http_response<R: Runtime>(
|
||||
update_source: &UpdateSource,
|
||||
) -> Result<HttpResponse> {
|
||||
let responses = list_http_responses_for_request(window, request_id, None).await?;
|
||||
for response in responses.iter().skip(MAX_HTTP_RESPONSES_PER_REQUEST - 1) {
|
||||
for response in responses.iter().skip(MAX_HISTORY_ITEMS - 1) {
|
||||
debug!("Deleting old response {}", response.id);
|
||||
delete_http_response(window, response.id.as_str(), update_source).await?;
|
||||
}
|
||||
@@ -2170,6 +2539,7 @@ pub struct BatchUpsertResult {
|
||||
pub folders: Vec<Folder>,
|
||||
pub http_requests: Vec<HttpRequest>,
|
||||
pub grpc_requests: Vec<GrpcRequest>,
|
||||
pub websocket_requests: Vec<WebsocketRequest>,
|
||||
}
|
||||
|
||||
pub async fn batch_upsert<R: Runtime>(
|
||||
@@ -2179,6 +2549,7 @@ pub async fn batch_upsert<R: Runtime>(
|
||||
folders: Vec<Folder>,
|
||||
http_requests: Vec<HttpRequest>,
|
||||
grpc_requests: Vec<GrpcRequest>,
|
||||
websocket_requests: Vec<WebsocketRequest>,
|
||||
update_source: &UpdateSource,
|
||||
) -> Result<BatchUpsertResult> {
|
||||
let mut imported_resources = BatchUpsertResult::default();
|
||||
@@ -2246,6 +2617,14 @@ pub async fn batch_upsert<R: Runtime>(
|
||||
info!("Imported {} grpc_requests", imported_resources.grpc_requests.len());
|
||||
}
|
||||
|
||||
if websocket_requests.len() > 0 {
|
||||
for v in websocket_requests {
|
||||
let x = upsert_websocket_request(&window, v, update_source).await?;
|
||||
imported_resources.websocket_requests.push(x.clone());
|
||||
}
|
||||
info!("Imported {} websocket_requests", imported_resources.websocket_requests.len());
|
||||
}
|
||||
|
||||
Ok(imported_resources)
|
||||
}
|
||||
|
||||
@@ -2264,6 +2643,7 @@ pub async fn get_workspace_export_resources<R: Runtime>(
|
||||
folders: Vec::new(),
|
||||
http_requests: Vec::new(),
|
||||
grpc_requests: Vec::new(),
|
||||
websocket_requests: Vec::new(),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -2273,6 +2653,7 @@ pub async fn get_workspace_export_resources<R: Runtime>(
|
||||
data.resources.folders.append(&mut list_folders(mgr, workspace_id).await?);
|
||||
data.resources.http_requests.append(&mut list_http_requests(mgr, workspace_id).await?);
|
||||
data.resources.grpc_requests.append(&mut list_grpc_requests(mgr, workspace_id).await?);
|
||||
data.resources.websocket_requests.append(&mut list_websocket_requests(mgr, workspace_id).await?);
|
||||
}
|
||||
|
||||
// Nuke environments if we don't want them
|
||||
|
||||
34
src-tauri/yaak-models/src/render.rs
Normal file
34
src-tauri/yaak-models/src/render.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
use std::collections::HashMap;
|
||||
use crate::models::{Environment, EnvironmentVariable};
|
||||
|
||||
pub fn make_vars_hashmap(
|
||||
base_environment: &Environment,
|
||||
environment: Option<&Environment>,
|
||||
) -> HashMap<String, String> {
|
||||
let mut variables = HashMap::new();
|
||||
variables = add_variable_to_map(variables, &base_environment.variables);
|
||||
|
||||
if let Some(e) = environment {
|
||||
variables = add_variable_to_map(variables, &e.variables);
|
||||
}
|
||||
|
||||
variables
|
||||
}
|
||||
|
||||
fn add_variable_to_map(
|
||||
m: HashMap<String, String>,
|
||||
variables: &Vec<EnvironmentVariable>,
|
||||
) -> HashMap<String, String> {
|
||||
let mut map = m.clone();
|
||||
for variable in variables {
|
||||
if !variable.enabled || variable.value.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let name = variable.name.as_str();
|
||||
let value = variable.value.as_str();
|
||||
map.insert(name.into(), value.into());
|
||||
}
|
||||
|
||||
map
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user