Database access refactor (#190)

This commit is contained in:
Gregory Schier
2025-03-25 08:35:10 -07:00
committed by GitHub
parent 445c30f3a9
commit 1d37d46130
72 changed files with 4895 additions and 4702 deletions

View File

@@ -3,27 +3,30 @@ use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error("Render error: {0}")]
#[error(transparent)]
TemplateError(#[from] yaak_templates::error::Error),
#[error("Model error: {0}")]
#[error(transparent)]
ModelError(#[from] yaak_models::error::Error),
#[error("Sync error: {0}")]
#[error(transparent)]
SyncError(#[from] yaak_sync::error::Error),
#[error("Git error: {0}")]
#[error(transparent)]
GitError(#[from] yaak_git::error::Error),
#[error("Websocket error: {0}")]
#[error(transparent)]
WebsocketError(#[from] yaak_ws::error::Error),
#[error("License error: {0}")]
#[error(transparent)]
LicenseError(#[from] yaak_license::error::Error),
#[error("Plugin error: {0}")]
#[error(transparent)]
PluginError(#[from] yaak_plugins::error::Error),
#[error("Updater error: {0}")]
UpdaterError(#[from] tauri_plugin_updater::Error),
#[error("Request error: {0}")]
RequestError(#[from] reqwest::Error),

View File

@@ -1,8 +1,6 @@
use tauri::{AppHandle, Runtime};
use yaak_models::queries::{
get_key_value_int, get_key_value_string, set_key_value_int, set_key_value_string, UpdateSource,
};
use yaak_models::manager::QueryManagerExt;
use yaak_models::queries_legacy::UpdateSource;
const NAMESPACE: &str = "analytics";
const NUM_LAUNCHES_KEY: &str = "num_launches";
@@ -21,34 +19,28 @@ pub async fn store_launch_history<R: Runtime>(app_handle: &AppHandle<R>) -> Laun
let mut info = LaunchEventInfo::default();
info.num_launches = get_num_launches(app_handle).await + 1;
info.previous_version =
get_key_value_string(app_handle, NAMESPACE, last_tracked_version_key, "").await;
info.current_version = app_handle.package_info().version.to_string();
if info.previous_version.is_empty() {
} else {
info.launched_after_update = info.current_version != info.previous_version;
};
app_handle
.queries()
.with_tx(|tx| {
info.previous_version =
tx.get_key_value_string(NAMESPACE, last_tracked_version_key, "");
// Update key values
if !info.previous_version.is_empty() {
info.launched_after_update = info.current_version != info.previous_version;
};
set_key_value_string(
app_handle,
NAMESPACE,
last_tracked_version_key,
info.current_version.as_str(),
&UpdateSource::Background,
)
.await;
// Update key values
set_key_value_int(
app_handle,
NAMESPACE,
NUM_LAUNCHES_KEY,
info.num_launches,
&UpdateSource::Background,
)
.await;
let source = &UpdateSource::Background;
let version = info.current_version.as_str();
tx.set_key_value_string(NAMESPACE, last_tracked_version_key, version, source);
tx.set_key_value_int(NAMESPACE, NUM_LAUNCHES_KEY, info.num_launches, source);
Ok(())
})
.await
.unwrap();
info
}
@@ -66,5 +58,5 @@ pub fn get_os() -> &'static str {
}
pub async fn get_num_launches<R: Runtime>(app_handle: &AppHandle<R>) -> i32 {
get_key_value_int(app_handle, NAMESPACE, NUM_LAUNCHES_KEY, 0).await
app_handle.queries().connect().await.unwrap().get_key_value_int(NAMESPACE, NUM_LAUNCHES_KEY, 0)
}

View File

@@ -24,14 +24,12 @@ use tokio::fs::{create_dir_all, File};
use tokio::io::AsyncWriteExt;
use tokio::sync::watch::Receiver;
use tokio::sync::{oneshot, Mutex};
use yaak_models::manager::QueryManagerExt;
use yaak_models::models::{
Cookie, CookieJar, Environment, HttpRequest, HttpResponse, HttpResponseHeader,
HttpResponseState, ProxySetting, ProxySettingAuth,
};
use yaak_models::queries::{
get_base_environment, get_http_response, get_or_create_settings, get_workspace,
update_response_if_id, upsert_cookie_jar, UpdateSource,
};
use yaak_models::queries_legacy::UpdateSource;
use yaak_plugins::events::{
CallHttpAuthenticationRequest, HttpHeader, RenderPurpose, WindowContext,
};
@@ -48,10 +46,18 @@ pub async fn send_http_request<R: Runtime>(
) -> Result<HttpResponse> {
let app_handle = window.app_handle().clone();
let plugin_manager = app_handle.state::<PluginManager>();
let workspace = get_workspace(&app_handle, &unrendered_request.workspace_id).await?;
let base_environment =
get_base_environment(&app_handle, &unrendered_request.workspace_id).await?;
let settings = get_or_create_settings(&app_handle).await;
let update_source = &UpdateSource::from_window(&window);
let (settings, workspace) = {
let db = window.queries().connect().await?;
let settings = db.get_or_create_settings(update_source)?;
let workspace = db.get_workspace(&unrendered_request.workspace_id)?;
(settings, workspace)
};
let base_environment = app_handle
.queries()
.connect()
.await?
.get_base_environment(&unrendered_request.workspace_id)?;
let response_id = og_response.id.clone();
let response = Arc::new(Mutex::new(og_response.clone()));
@@ -534,8 +540,12 @@ pub async fn send_http_request<R: Runtime>(
};
r.state = HttpResponseState::Connected;
update_response_if_id(&app_handle, &r, &update_source)
app_handle
.queries()
.connect()
.await
.unwrap()
.update_http_response_if_id(&r, &update_source)
.expect("Failed to update response after connected");
}
@@ -563,8 +573,12 @@ pub async fn send_http_request<R: Runtime>(
f.flush().await.expect("Failed to flush file");
written_bytes += bytes.len();
r.content_length = Some(written_bytes as i32);
update_response_if_id(&app_handle, &r, &update_source)
app_handle
.queries()
.connect()
.await
.unwrap()
.update_http_response_if_id(&r, &update_source)
.expect("Failed to update response");
}
Ok(None) => {
@@ -591,8 +605,12 @@ pub async fn send_http_request<R: Runtime>(
None => Some(written_bytes as i32),
};
r.state = HttpResponseState::Closed;
update_response_if_id(&app_handle, &r, &UpdateSource::from_window(&window))
app_handle
.queries()
.connect()
.await
.unwrap()
.update_http_response_if_id(&r, &UpdateSource::from_window(&window))
.expect("Failed to update response");
};
@@ -617,12 +635,12 @@ pub async fn send_http_request<R: Runtime>(
})
.collect::<Vec<_>>();
cookie_jar.cookies = json_cookies;
if let Err(e) = upsert_cookie_jar(
&app_handle,
&cookie_jar,
&UpdateSource::from_window(&window),
)
.await
if let Err(e) = app_handle
.queries()
.connect()
.await
.unwrap()
.upsert_cookie_jar(&cookie_jar, &UpdateSource::from_window(&window))
{
error!("Failed to update cookie jar: {}", e);
};
@@ -649,10 +667,16 @@ pub async fn send_http_request<R: Runtime>(
Ok(tokio::select! {
Ok(r) = done_rx => r,
_ = cancelled_rx.changed() => {
match get_http_response(&app_handle, response_id.as_str()).await {
match app_handle.queries().with_conn(|c| c.get_http_response(&response_id)).await {
Ok(mut r) => {
r.state = HttpResponseState::Closed;
update_response_if_id(&app_handle, &r, &UpdateSource::from_window(window)).await.expect("Failed to update response")
app_handle
.queries()
.connect()
.await
.unwrap()
.update_http_response_if_id(&r, &UpdateSource::from_window(window))
.expect("Failed to update response")
},
_ => {
response_err(&app_handle, &*response.lock().await, "Ephemeral request was cancelled".to_string(), &update_source).await

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,8 @@ use reqwest::Method;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use tauri::{AppHandle, Emitter, Manager, Runtime, WebviewWindow};
use yaak_models::queries::{get_key_value_raw, set_key_value_raw, UpdateSource};
use yaak_models::manager::QueryManagerExt;
use yaak_models::queries_legacy::UpdateSource;
// Check for updates every hour
const MAX_UPDATE_CHECK_SECONDS: u64 = 60 * 60;
@@ -43,13 +44,22 @@ impl YaakNotifier {
}
}
pub async fn seen<R: Runtime>(&mut self, window: &WebviewWindow<R>, id: &str) -> Result<(), String> {
pub async fn seen<R: Runtime>(
&mut self,
window: &WebviewWindow<R>,
id: &str,
) -> Result<(), String> {
let app_handle = window.app_handle();
let mut seen = get_kv(app_handle).await?;
seen.push(id.to_string());
debug!("Marked notification as seen {}", id);
let seen_json = serde_json::to_string(&seen).map_err(|e| e.to_string())?;
set_key_value_raw(app_handle, KV_NAMESPACE, KV_KEY, seen_json.as_str(), &UpdateSource::from_window(window)).await;
window.queries().connect().await.map_err(|e| e.to_string())?.set_key_value_raw(
KV_NAMESPACE,
KV_KEY,
seen_json.as_str(),
&UpdateSource::from_window(window),
);
Ok(())
}
@@ -70,7 +80,7 @@ impl YaakNotifier {
.query(&[
("version", info.version.to_string().as_str()),
("launches", num_launches.to_string().as_str()),
("platform", get_os())
("platform", get_os()),
]);
let resp = req.send().await.map_err(|e| e.to_string())?;
if resp.status() != 200 {
@@ -108,7 +118,13 @@ impl YaakNotifier {
}
async fn get_kv<R: Runtime>(app_handle: &AppHandle<R>) -> Result<Vec<String>, String> {
match get_key_value_raw(app_handle, "notifications", "seen").await {
match app_handle
.queries()
.connect()
.await
.map_err(|e| e.to_string())?
.get_key_value_raw("notifications", "seen")
{
None => Ok(Vec::new()),
Some(v) => serde_json::from_str(&v.value).map_err(|e| e.to_string()),
}

View File

@@ -9,12 +9,9 @@ use chrono::Utc;
use log::warn;
use tauri::{AppHandle, Emitter, Manager, Runtime, State};
use tauri_plugin_clipboard_manager::ClipboardExt;
use yaak_models::manager::QueryManagerExt;
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_models::queries_legacy::UpdateSource;
use yaak_plugins::events::{
Color, DeleteKeyValueResponse, EmptyPayload, FindHttpResponsesResponse,
GetHttpRequestByIdResponse, GetKeyValueResponse, Icon, InternalEvent, InternalEventPayload,
@@ -55,19 +52,28 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
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();
let http_responses = app_handle
.queries()
.connect()
.await
.unwrap()
.list_http_responses_for_request(
req.request_id.as_str(),
req.limit.map(|l| l as u64),
)
.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();
let http_request = app_handle
.queries()
.connect()
.await
.unwrap()
.get_http_request(req.id.as_str())
.unwrap();
Some(InternalEventPayload::GetHttpRequestByIdResponse(GetHttpRequestByIdResponse {
http_request,
}))
@@ -80,8 +86,12 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
.await
.expect("Failed to get workspace_id from window URL");
let environment = environment_from_window(&window).await;
let base_environment = get_base_environment(app_handle, workspace.id.as_str())
let base_environment = app_handle
.queries()
.connect()
.await
.unwrap()
.get_base_environment(&workspace.id)
.expect("Failed to get base environment");
let cb = PluginTemplateCallback::new(app_handle, &window_context, req.purpose);
let http_request = render_http_request(
@@ -104,8 +114,12 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
.await
.expect("Failed to get workspace_id from window URL");
let environment = environment_from_window(&window).await;
let base_environment = get_base_environment(app_handle, workspace.id.as_str())
let base_environment = app_handle
.queries()
.connect()
.await
.unwrap()
.get_base_environment(&workspace.id)
.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)
@@ -135,7 +149,7 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
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();
let plugins = app_handle.queries().connect().await.unwrap().list_plugins().unwrap();
for plugin in plugins {
if plugin.directory != plugin_handle.dir {
continue;
@@ -145,7 +159,13 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
updated_at: Utc::now().naive_utc(), // TODO: Add reloaded_at field to use instead
..plugin
};
upsert_plugin(app_handle, new_plugin, &UpdateSource::Plugin).await.unwrap();
app_handle
.queries()
.connect()
.await
.unwrap()
.upsert_plugin(&new_plugin, &UpdateSource::Plugin)
.unwrap();
}
let toast_event = plugin_handle.build_event_to_send(
&WindowContext::from_window(&window),
@@ -173,22 +193,29 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
http_request.workspace_id = workspace.id;
}
let resp = if http_request.id.is_empty() {
HttpResponse::new()
let http_response = if http_request.id.is_empty() {
HttpResponse::default()
} else {
create_default_http_response(
app_handle,
http_request.id.as_str(),
&UpdateSource::Plugin,
)
.await
.unwrap()
window
.queries()
.connect()
.await
.unwrap()
.upsert_http_response(
&HttpResponse {
request_id: http_request.id.clone(),
workspace_id: http_request.workspace_id.clone(),
..Default::default()
},
&UpdateSource::Plugin,
)
.unwrap()
};
let result = send_http_request(
&window,
&http_request,
&resp,
&http_response,
environment,
cookie_jar,
&mut tokio::sync::watch::channel(false).1, // No-op cancel channel
@@ -265,17 +292,34 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
}
InternalEventPayload::SetKeyValueRequest(req) => {
let name = plugin_handle.name().await;
set_plugin_key_value(app_handle, &name, &req.key, &req.value).await;
app_handle
.queries()
.connect()
.await
.unwrap()
.set_plugin_key_value(&name, &req.key, &req.value);
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);
let value = app_handle
.queries()
.connect()
.await
.unwrap()
.get_plugin_key_value(&name, &req.key)
.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;
let deleted = app_handle
.queries()
.connect()
.await
.unwrap()
.delete_plugin_key_value(&name, &req.key)
.unwrap();
Some(InternalEventPayload::DeleteKeyValueResponse(DeleteKeyValueResponse { deleted }))
}
_ => None,

View File

@@ -1,12 +1,14 @@
use std::fmt::{Display, Formatter};
use std::time::SystemTime;
use crate::error::Result;
use log::info;
use tauri::{AppHandle, Manager};
use tauri::{Manager, Runtime, WebviewWindow};
use tauri_plugin_dialog::{DialogExt, MessageDialogButtons};
use tauri_plugin_updater::UpdaterExt;
use tokio::task::block_in_place;
use yaak_models::queries::get_or_create_settings;
use yaak_models::manager::QueryManagerExt;
use yaak_models::queries_legacy::UpdateSource;
use yaak_plugins::manager::PluginManager;
use crate::is_dev;
@@ -59,30 +61,34 @@ impl YaakUpdater {
}
}
pub async fn check_now(
pub async fn check_now<R: Runtime>(
&mut self,
app_handle: &AppHandle,
window: &WebviewWindow<R>,
mode: UpdateMode,
update_trigger: UpdateTrigger,
) -> Result<bool, tauri_plugin_updater::Error> {
let settings = get_or_create_settings(app_handle).await;
) -> Result<bool> {
let settings = window
.queries()
.connect()
.await?
.get_or_create_settings(&UpdateSource::from_window(window))?;
let update_key = format!("{:x}", md5::compute(settings.id));
self.last_update_check = SystemTime::now();
info!("Checking for updates mode={}", mode);
let h = app_handle.clone();
let update_check_result = app_handle
let w = window.clone();
let update_check_result = w
.updater_builder()
.on_before_exit(move || {
// Kill plugin manager before exit or NSIS installer will fail to replace sidecar
// while it's running.
// NOTE: This is only called on Windows
let h = h.clone();
let w = w.clone();
block_in_place(|| {
tauri::async_runtime::block_on(async move {
info!("Shutting down plugin manager before update");
let plugin_manager = h.state::<PluginManager>();
let plugin_manager = w.state::<PluginManager>();
plugin_manager.terminate().await;
});
});
@@ -100,11 +106,11 @@ impl YaakUpdater {
.check()
.await;
match update_check_result {
Ok(Some(update)) => {
let h = app_handle.clone();
app_handle
.dialog()
let result = match update_check_result? {
None => false,
Some(update) => {
let w = window.clone();
w.dialog()
.message(format!(
"{} is available. Would you like to download and install it now?",
update.version
@@ -121,7 +127,7 @@ impl YaakUpdater {
tauri::async_runtime::spawn(async move {
match update.download_and_install(|_, _| {}, || {}).await {
Ok(_) => {
if h.dialog()
if w.dialog()
.message("Would you like to restart the app?")
.title("Update Installed")
.buttons(MessageDialogButtons::OkCancelCustom(
@@ -130,27 +136,27 @@ impl YaakUpdater {
))
.blocking_show()
{
h.restart();
w.app_handle().restart();
}
}
Err(e) => {
h.dialog()
w.dialog()
.message(format!("The update failed to install: {}", e));
}
}
});
});
Ok(true)
true
}
Ok(None) => Ok(false),
Err(e) => Err(e),
}
};
Ok(result)
}
pub async fn maybe_check(
pub async fn maybe_check<R: Runtime>(
&mut self,
app_handle: &AppHandle,
window: &WebviewWindow<R>,
mode: UpdateMode,
) -> Result<bool, tauri_plugin_updater::Error> {
) -> Result<bool> {
let update_period_seconds = match mode {
UpdateMode::Stable => MAX_UPDATE_CHECK_HOURS_STABLE,
UpdateMode::Beta => MAX_UPDATE_CHECK_HOURS_BETA,
@@ -167,6 +173,6 @@ impl YaakUpdater {
return Ok(false);
}
self.check_now(app_handle, mode, UpdateTrigger::Background).await
self.check_now(window, mode, UpdateTrigger::Background).await
}
}