feat(import): merge re-imports from a linked source instead of duplicating (#618)

This commit is contained in:
Gregory Schier
2026-09-01 11:12:07 -07:00
committed by GitHub
parent d461c982ec
commit d2d4b80a09
28 changed files with 2504 additions and 275 deletions
+28 -6
View File
@@ -6,13 +6,16 @@ use std::io::ErrorKind;
use tauri::{Manager, Runtime, WebviewWindow};
use yaak::import::{self, PlanImportDataParams};
use yaak_api::{ApiClientKind, yaak_api_client};
use yaak_models::util::{BatchUpsertResult, ImportDestination, ImportPlan};
use yaak_models::util::{BatchUpsertResult, ImportDestination, ImportOrigin, ImportPlan};
pub(crate) async fn import_data<R: Runtime>(
window: &WebviewWindow<R>,
file_path: &str,
origin: Option<ImportOrigin>,
) -> Result<BatchUpsertResult> {
let plan = plan_import_data(window, file_path, ImportDestination::NewWorkspace).await?;
let contents = read_import_file(file_path)?;
let plan =
plan_import_contents(window, &contents, ImportDestination::NewWorkspace, origin).await?;
commit_import(window, plan)
}
@@ -22,7 +25,7 @@ pub(crate) async fn plan_import_data<R: Runtime>(
destination: ImportDestination,
) -> Result<ImportPlan> {
let contents = read_import_file(file_path)?;
plan_import_contents(window, &contents, destination).await
plan_import_contents(window, &contents, destination, Some(file_origin(file_path))).await
}
pub(crate) async fn plan_import_url<R: Runtime>(
@@ -30,14 +33,16 @@ pub(crate) async fn plan_import_url<R: Runtime>(
url: &str,
destination: ImportDestination,
) -> Result<ImportPlan> {
let contents = fetch_import_url(window, url).await?;
plan_import_contents(window, &contents, destination).await
let url = normalize_import_url(url)?;
let contents = fetch_import_url(window, &url).await?;
plan_import_contents(window, &contents, destination, Some(url_origin(&url))).await
}
async fn plan_import_contents<R: Runtime>(
window: &WebviewWindow<R>,
contents: &str,
destination: ImportDestination,
origin: Option<ImportOrigin>,
) -> Result<ImportPlan> {
let plugin_manager = crate::plugins_ext::plugin_manager(window).await?;
let query_manager = window.db_manager();
@@ -49,10 +54,27 @@ async fn plan_import_contents<R: Runtime>(
plugin_context: &plugin_context,
destination,
contents,
origin,
})
.await?)
}
/// Canonicalize so re-importing the same file through a different spelling of its path still
/// matches the linked source.
pub(crate) fn file_origin(file_path: &str) -> ImportOrigin {
let path = std::path::Path::new(file_path);
let canonical = std::fs::canonicalize(path).unwrap_or_else(|_| path.to_path_buf());
let label = canonical
.file_name()
.map(|name| name.to_string_lossy().to_string())
.unwrap_or_else(|| file_path.to_string());
ImportOrigin { origin: canonical.to_string_lossy().to_string(), label }
}
pub(crate) fn url_origin(url: &str) -> ImportOrigin {
ImportOrigin { origin: url.to_string(), label: url.to_string() }
}
pub(crate) fn commit_import<R: Runtime>(
window: &WebviewWindow<R>,
plan: ImportPlan,
@@ -88,7 +110,7 @@ async fn fetch_import_url<R: Runtime>(window: &WebviewWindow<R>, url: &str) -> R
.map_err(|err| Error::GenericError(format!("Failed to read response from {url}: {err}")))
}
fn normalize_import_url(url: &str) -> Result<String> {
pub(crate) fn normalize_import_url(url: &str) -> Result<String> {
let url = url.trim();
if url.is_empty() {
return Err(Error::GenericError("Import URL must not be empty".to_string()));
+20 -1
View File
@@ -37,7 +37,8 @@ use yaak_grpc::ServiceDefinition;
use yaak_models::blob_manager::BlobManager;
use yaak_models::models::{
GraphQlIntrospection, GrpcEvent, HttpRequest, HttpRequestHeader, HttpResponse,
HttpResponseEvent, Plugin, Settings, WebsocketConnection, WebsocketEvent, WorkspaceMeta,
HttpResponseEvent, ImportSource, Plugin, Settings, WebsocketConnection, WebsocketEvent,
WorkspaceMeta,
};
use yaak_models::query_manager::QueryManager;
use yaak_models::util::{BatchUpsertResult, ImportPlan};
@@ -464,6 +465,24 @@ async fn cmd_commit_import<R: Runtime>(ctx: ClientCtx<R>, req: CmdCommitImportRe
Ok(crate::cmd_commit_import(ctx.window.clone(), req.plan).await?)
}
async fn cmd_list_import_sources<R: Runtime>(ctx: ClientCtx<R>, req: CmdListImportSourcesReq) -> Result<Vec<ImportSource>> {
use crate::models_ext::QueryManagerExt;
Ok(ctx.window.db().list_import_sources(&req.workspace_id)?)
}
async fn cmd_import_sources_for_origin<R: Runtime>(ctx: ClientCtx<R>, req: CmdImportSourcesForOriginReq) -> Result<Vec<ImportSource>> {
use crate::models_ext::QueryManagerExt;
let origin = match (req.file_path, req.url) {
(Some(file_path), _) => crate::import::file_origin(&file_path).origin,
(None, Some(url)) => match crate::import::normalize_import_url(&url) {
Ok(url) => crate::import::url_origin(&url).origin,
Err(_) => return Ok(Vec::new()),
},
(None, None) => return Ok(Vec::new()),
};
Ok(ctx.window.db().list_import_sources_by_origin(&origin)?)
}
async fn cmd_http_request_actions<R: Runtime>(ctx: ClientCtx<R>, req: CmdHttpRequestActionsReq) -> Result<Vec<GetHttpRequestActionsResponse>> {
Ok(yaak_commands::actions::cmd_http_request_actions(ctx, req).await?)
}
@@ -1,6 +1,6 @@
use crate::PluginContextExt;
use crate::error::Result;
use crate::import::import_data;
use crate::import::{file_origin, import_data, url_origin};
use crate::models_ext::QueryManagerExt;
use log::{info, warn};
use std::collections::HashMap;
@@ -69,6 +69,7 @@ pub(crate) async fn handle_deep_link<R: Runtime>(
}
"import-data" => {
let mut file_path = query_map.get("path").map(|s| s.to_owned());
let mut origin = None;
let name = query_map.get("name").map(|s| s.to_owned()).unwrap_or("data".to_string());
_ = window.set_focus();
@@ -98,6 +99,7 @@ pub(crate) async fn handle_deep_link<R: Runtime>(
.to_string();
fs::write(&p, json)?;
file_path = Some(p);
origin = Some(url_origin(file_url));
}
let file_path = match file_path {
@@ -116,7 +118,8 @@ pub(crate) async fn handle_deep_link<R: Runtime>(
}
};
let results = import_data(window, &file_path).await?;
let origin = origin.unwrap_or_else(|| file_origin(&file_path));
let results = import_data(window, &file_path, Some(origin)).await?;
window.emit(
"show_toast",
ShowToastRequest {