Add CLI import and export commands (#484)

This commit is contained in:
Gregory Schier
2026-06-29 11:43:20 -07:00
committed by GitHub
parent 9ffd8d4810
commit 18b983bfe5
15 changed files with 592 additions and 126 deletions
Generated
+2
View File
@@ -10052,6 +10052,7 @@ dependencies = [
"tempfile",
"thiserror 2.0.17",
"tokio",
"yaak-core",
"yaak-crypto",
"yaak-http",
"yaak-models",
@@ -10182,6 +10183,7 @@ dependencies = [
"webbrowser",
"yaak",
"yaak-api",
"yaak-core",
"yaak-crypto",
"yaak-http",
"yaak-models",
+1
View File
@@ -42,6 +42,7 @@ webbrowser = "1"
zip = "4"
yaak = { workspace = true }
yaak-api = { workspace = true }
yaak-core = { workspace = true }
yaak-crypto = { workspace = true }
yaak-http = { workspace = true }
yaak-models = { workspace = true }
+34
View File
@@ -42,6 +42,12 @@ pub enum Commands {
/// Authentication commands
Auth(AuthArgs),
/// Import API data from Yaak, OpenAPI, Postman, Insomnia, Swagger, or cURL
Import(ImportArgs),
/// Export Yaak workspace data
Export(ExportArgs),
/// Plugin development and publishing commands
Plugin(PluginArgs),
@@ -92,6 +98,34 @@ pub struct SendArgs {
pub fail_fast: bool,
}
#[derive(Args)]
pub struct ImportArgs {
/// Path to the file to import
pub file: PathBuf,
/// Existing workspace ID to import into when supported by the importer
#[arg(long = "workspace-id", value_name = "WORKSPACE_ID")]
pub workspace_id: Option<String>,
}
#[derive(Args)]
pub struct ExportArgs {
/// Path to write the Yaak export JSON file
pub file: PathBuf,
/// Workspace IDs to export (defaults to the only workspace when exactly one exists)
#[arg(value_name = "WORKSPACE_ID")]
pub workspace_ids: Vec<String>,
/// Export all workspaces
#[arg(long, conflicts_with = "workspace_ids")]
pub all: bool,
/// Include private environments in the export
#[arg(long)]
pub include_private_environments: bool,
}
#[derive(Args)]
#[command(disable_help_subcommand = true)]
pub struct CookieJarArgs {
@@ -0,0 +1,176 @@
use crate::cli::{ExportArgs, ImportArgs};
use crate::context::CliContext;
use crate::utils::workspace::resolve_workspace_id;
use std::fs;
use std::io::ErrorKind;
use yaak::export::{self, ExportDataParams};
use yaak::import;
use yaak_core::WorkspaceContext;
use yaak_models::util::BatchUpsertResult;
use yaak_plugins::events::{ImportResources, PluginContext};
type CommandResult<T = ()> = std::result::Result<T, String>;
pub async fn run_import(ctx: &CliContext, args: ImportArgs) -> i32 {
match import(ctx, args).await {
Ok(result) => {
println!("Imported {}", format_counts(&result));
0
}
Err(error) => {
eprintln!("Error: {error}");
1
}
}
}
pub fn run_export(ctx: &CliContext, args: ExportArgs) -> i32 {
match export(ctx, args) {
Ok(count) => {
println!("Exported {count} workspace(s)");
0
}
Err(error) => {
eprintln!("Error: {error}");
1
}
}
}
async fn import(ctx: &CliContext, args: ImportArgs) -> CommandResult<BatchUpsertResult> {
if let Some(workspace_id) = args.workspace_id.as_deref() {
ctx.db()
.get_workspace(workspace_id)
.map_err(|e| format!("Failed to get workspace '{workspace_id}': {e}"))?;
}
let file_contents = read_import_file(&args.file)?;
let plugin_context = PluginContext::new(None, args.workspace_id.clone());
let plugin_manager = ctx.plugin_manager();
let import_result = plugin_manager
.import_data(&plugin_context, &file_contents)
.await
.map_err(|e| format!("Failed to import data: {e}"))?;
let resources = import_result.resources;
let workspace_id = args.workspace_id;
if workspace_id.is_none() && resources_need_current_workspace(&resources) {
return Err(
"This import requires a workspace context. Provide --workspace-id <WORKSPACE_ID>."
.to_string(),
);
}
let workspace_context = WorkspaceContext {
workspace_id,
environment_id: None,
cookie_jar_id: None,
request_id: None,
};
let imported = import::import_resources(ctx.query_manager(), workspace_context, resources)
.map_err(|e| format!("Failed to import data: {e}"))?;
Ok(imported)
}
fn export(ctx: &CliContext, args: ExportArgs) -> CommandResult<usize> {
let workspace_ids = resolve_export_workspace_ids(ctx, args.workspace_ids, args.all)?;
let workspace_id_refs: Vec<&str> = workspace_ids.iter().map(String::as_str).collect();
export::export_data(ExportDataParams {
query_manager: ctx.query_manager(),
yaak_version: env!("CARGO_PKG_VERSION"),
export_path: &args.file,
workspace_ids: workspace_id_refs,
include_private_environments: args.include_private_environments,
})
.map_err(|e| format!("Failed to export data: {e}"))?;
Ok(workspace_ids.len())
}
fn resolve_export_workspace_ids(
ctx: &CliContext,
workspace_ids: Vec<String>,
all: bool,
) -> CommandResult<Vec<String>> {
if all {
let workspaces =
ctx.db().list_workspaces().map_err(|e| format!("Failed to list workspaces: {e}"))?;
if workspaces.is_empty() {
return Err("No workspaces found to export".to_string());
}
return Ok(workspaces.into_iter().map(|w| w.id).collect());
}
if workspace_ids.is_empty() {
return resolve_workspace_id(ctx, None, "export").map(|id| vec![id]);
}
for workspace_id in &workspace_ids {
ctx.db()
.get_workspace(workspace_id)
.map_err(|e| format!("Failed to get workspace '{workspace_id}': {e}"))?;
}
Ok(workspace_ids)
}
fn read_import_file(path: &std::path::Path) -> CommandResult<String> {
fs::read_to_string(path).map_err(|err| {
if err.kind() == ErrorKind::InvalidData {
format!(
"Import file must be UTF-8 text; binary files are not supported: {}",
path.display()
)
} else {
format!("Unable to read import file {}: {err}", path.display())
}
})
}
fn resources_need_current_workspace(resources: &ImportResources) -> bool {
resources.workspaces.iter().any(|w| w.id == "CURRENT_WORKSPACE")
|| resources.environments.iter().any(|e| {
e.workspace_id == "CURRENT_WORKSPACE"
|| e.parent_id.as_deref() == Some("CURRENT_WORKSPACE")
})
|| resources.folders.iter().any(|f| {
f.workspace_id == "CURRENT_WORKSPACE"
|| f.folder_id.as_deref() == Some("CURRENT_WORKSPACE")
})
|| resources.http_requests.iter().any(|r| {
r.workspace_id == "CURRENT_WORKSPACE"
|| r.folder_id.as_deref() == Some("CURRENT_WORKSPACE")
})
|| resources.grpc_requests.iter().any(|r| {
r.workspace_id == "CURRENT_WORKSPACE"
|| r.folder_id.as_deref() == Some("CURRENT_WORKSPACE")
})
|| resources.websocket_requests.iter().any(|r| {
r.workspace_id == "CURRENT_WORKSPACE"
|| r.folder_id.as_deref() == Some("CURRENT_WORKSPACE")
})
}
fn format_counts(result: &BatchUpsertResult) -> String {
let names = [
"workspace",
"environment",
"folder",
"HTTP request",
"gRPC request",
"WebSocket request",
];
let counts = [
(result.workspaces.len(), names[0]),
(result.environments.len(), names[1]),
(result.folders.len(), names[2]),
(result.http_requests.len(), names[3]),
(result.grpc_requests.len(), names[4]),
(result.websocket_requests.len(), names[5]),
];
let non_zero: Vec<String> = counts
.into_iter()
.filter(|(count, _)| *count > 0)
.map(|(count, name)| format!("{count} {name}{}", if count == 1 { "" } else { "s" }))
.collect();
if non_zero.is_empty() { "nothing".to_string() } else { non_zero.join(", ") }
}
+1
View File
@@ -2,6 +2,7 @@ pub mod auth;
pub mod cookie_jar;
pub mod environment;
pub mod folder;
pub mod import_export;
pub mod plugin;
pub mod request;
pub mod send;
+17
View File
@@ -37,6 +37,23 @@ async fn main() {
let exit_code = match command {
Commands::Auth(args) => commands::auth::run(args).await,
Commands::Import(args) => {
let mut context = CliContext::new(data_dir.clone(), app_id);
let execution_context = CliExecutionContext {
workspace_id: args.workspace_id.clone(),
..CliExecutionContext::default()
};
context.init_plugins(execution_context).await;
let exit_code = commands::import_export::run_import(&context, args).await;
context.shutdown().await;
exit_code
}
Commands::Export(args) => {
let context = CliContext::new(data_dir.clone(), app_id);
let exit_code = commands::import_export::run_export(&context, args);
context.shutdown().await;
exit_code
}
Commands::Plugin(args) => match args.command {
PluginCommands::Build(args) => commands::plugin::run_build(args).await,
PluginCommands::Dev(args) => commands::plugin::run_dev(args).await,
@@ -0,0 +1,162 @@
mod common;
use common::{cli_cmd, parse_created_id, query_manager, seed_request};
use predicates::str::contains;
use serde_json::Value;
use tempfile::TempDir;
#[test]
fn export_writes_yaak_workspace_file() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let data_dir = temp_dir.path();
let export_path = temp_dir.path().join("export.json");
let create_assert =
cli_cmd(data_dir).args(["workspace", "create", "--name", "Export Me"]).assert().success();
let workspace_id = parse_created_id(&create_assert.get_output().stdout, "workspace create");
seed_request(data_dir, &workspace_id, "req_export");
cli_cmd(data_dir)
.args([
"export",
export_path.to_str().expect("export path is utf-8"),
&workspace_id,
])
.assert()
.success()
.stdout(contains("Exported 1 workspace(s)"));
let exported: Value = serde_json::from_str(
&std::fs::read_to_string(export_path).expect("export file should exist"),
)
.expect("export should be JSON");
assert_eq!(exported["yaakSchema"], 4);
assert_eq!(exported["resources"]["workspaces"][0]["id"], workspace_id);
assert_eq!(exported["resources"]["httpRequests"][0]["id"], "req_export");
}
#[test]
fn import_reads_yaak_workspace_file() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let data_dir = temp_dir.path();
let import_path = temp_dir.path().join("import.json");
std::fs::write(
&import_path,
r#"{
"yaakVersion": "test",
"yaakSchema": 4,
"resources": {
"workspaces": [
{
"model": "workspace",
"id": "wrk_import",
"name": "Imported Workspace"
}
],
"httpRequests": [
{
"model": "http_request",
"id": "req_import",
"workspaceId": "wrk_import",
"name": "Imported Request",
"method": "GET",
"url": "https://example.com"
}
]
}
}"#,
)
.expect("write import fixture");
cli_cmd(data_dir)
.args([
"import",
import_path.to_str().expect("import path is utf-8"),
])
.assert()
.success()
.stdout(contains("Imported 1 workspace, 1 HTTP request"));
let query_manager = query_manager(data_dir);
let db = query_manager.connect();
assert_eq!(
db.get_workspace("wrk_import").expect("workspace imported").name,
"Imported Workspace"
);
assert_eq!(
db.get_http_request("req_import").expect("request imported").url,
"https://example.com"
);
}
fn write_postman_environment_fixture(path: &std::path::Path) {
std::fs::write(
path,
r#"{
"name": "Local",
"_postman_variable_scope": "environment",
"values": [
{
"key": "token",
"value": "abc123",
"enabled": true
}
]
}"#,
)
.expect("write postman environment fixture");
}
#[test]
fn import_postman_environment_requires_workspace_id() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let data_dir = temp_dir.path();
let import_path = temp_dir.path().join("postman-env.json");
cli_cmd(data_dir).args(["workspace", "create", "--name", "Env Target"]).assert().success();
write_postman_environment_fixture(&import_path);
cli_cmd(data_dir)
.args([
"import",
import_path.to_str().expect("import path is utf-8"),
])
.assert()
.failure()
.stderr(contains("requires a workspace context"))
.stderr(contains("--workspace-id"));
}
#[test]
fn import_postman_environment_uses_workspace_id() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let data_dir = temp_dir.path();
let import_path = temp_dir.path().join("postman-env.json");
let create_assert =
cli_cmd(data_dir).args(["workspace", "create", "--name", "Env Target"]).assert().success();
let workspace_id = parse_created_id(&create_assert.get_output().stdout, "workspace create");
write_postman_environment_fixture(&import_path);
cli_cmd(data_dir)
.args([
"import",
import_path.to_str().expect("import path is utf-8"),
"--workspace-id",
&workspace_id,
])
.assert()
.success()
.stdout(contains("Imported 1 environment"));
let query_manager = query_manager(data_dir);
let db = query_manager.connect();
let environments =
db.list_environments_ensure_base(&workspace_id).expect("list imported environments");
let imported_environment =
environments.iter().find(|e| e.name == "Local").expect("postman environment imported");
assert_eq!(imported_environment.workspace_id, workspace_id);
}
@@ -38,6 +38,9 @@ pub enum Error {
#[error(transparent)]
ApiError(#[from] yaak_api::Error),
#[error(transparent)]
YaakError(#[from] yaak::Error),
#[error(transparent)]
ClipboardError(#[from] tauri_plugin_clipboard_manager::Error),
+13 -106
View File
@@ -1,16 +1,12 @@
use crate::PluginContextExt;
use crate::error::{Error, Result};
use crate::models_ext::QueryManagerExt;
use log::info;
use std::collections::BTreeMap;
use std::fs::read_to_string;
use std::io::ErrorKind;
use tauri::{Manager, Runtime, WebviewWindow};
use yaak::import::{self, ImportDataParams};
use yaak_core::WorkspaceContext;
use yaak_models::models::{
Environment, Folder, GrpcRequest, HttpRequest, WebsocketRequest, Workspace,
};
use yaak_models::util::{BatchUpsertResult, UpdateSource, maybe_gen_id, maybe_gen_id_opt};
use yaak_models::util::BatchUpsertResult;
use yaak_plugins::manager::PluginManager;
use yaak_tauri_utils::window::WorkspaceWindowTrait;
@@ -19,113 +15,24 @@ pub(crate) async fn import_data<R: Runtime>(
file_path: &str,
) -> Result<BatchUpsertResult> {
let plugin_manager = window.state::<PluginManager>();
let query_manager = window.db_manager();
let file = read_import_file(file_path)?;
let file_contents = file.as_str();
let import_result = plugin_manager.import_data(&window.plugin_context(), file_contents).await?;
let mut id_map: BTreeMap<String, String> = BTreeMap::new();
// Create WorkspaceContext from window
let ctx = WorkspaceContext {
let plugin_context = window.plugin_context();
let workspace_context = WorkspaceContext {
workspace_id: window.workspace_id(),
environment_id: window.environment_id(),
cookie_jar_id: window.cookie_jar_id(),
request_id: None,
};
let resources = import_result.resources;
let workspaces: Vec<Workspace> = resources
.workspaces
.into_iter()
.map(|mut v| {
v.id = maybe_gen_id::<Workspace>(&ctx, v.id.as_str(), &mut id_map);
v
})
.collect();
let environments: Vec<Environment> = resources
.environments
.into_iter()
.map(|mut v| {
v.id = maybe_gen_id::<Environment>(&ctx, v.id.as_str(), &mut id_map);
v.workspace_id = maybe_gen_id::<Workspace>(&ctx, v.workspace_id.as_str(), &mut id_map);
match (v.parent_model.as_str(), v.parent_id.clone().as_deref()) {
("folder", Some(parent_id)) => {
v.parent_id = Some(maybe_gen_id::<Folder>(&ctx, &parent_id, &mut id_map));
}
("", _) => {
// Fix any empty ones
v.parent_model = "workspace".to_string();
}
_ => {
// Parent ID only required for the folder case
v.parent_id = None;
}
};
v
})
.collect();
let folders: Vec<Folder> = resources
.folders
.into_iter()
.map(|mut v| {
v.id = maybe_gen_id::<Folder>(&ctx, v.id.as_str(), &mut id_map);
v.workspace_id = maybe_gen_id::<Workspace>(&ctx, v.workspace_id.as_str(), &mut id_map);
v.folder_id = maybe_gen_id_opt::<Folder>(&ctx, v.folder_id, &mut id_map);
v
})
.collect();
let http_requests: Vec<HttpRequest> = resources
.http_requests
.into_iter()
.map(|mut v| {
v.id = maybe_gen_id::<HttpRequest>(&ctx, v.id.as_str(), &mut id_map);
v.workspace_id = maybe_gen_id::<Workspace>(&ctx, v.workspace_id.as_str(), &mut id_map);
v.folder_id = maybe_gen_id_opt::<Folder>(&ctx, v.folder_id, &mut id_map);
v
})
.collect();
let grpc_requests: Vec<GrpcRequest> = resources
.grpc_requests
.into_iter()
.map(|mut v| {
v.id = maybe_gen_id::<GrpcRequest>(&ctx, v.id.as_str(), &mut id_map);
v.workspace_id = maybe_gen_id::<Workspace>(&ctx, v.workspace_id.as_str(), &mut id_map);
v.folder_id = maybe_gen_id_opt::<Folder>(&ctx, v.folder_id, &mut id_map);
v
})
.collect();
let websocket_requests: Vec<WebsocketRequest> = resources
.websocket_requests
.into_iter()
.map(|mut v| {
v.id = maybe_gen_id::<WebsocketRequest>(&ctx, v.id.as_str(), &mut id_map);
v.workspace_id = maybe_gen_id::<Workspace>(&ctx, v.workspace_id.as_str(), &mut id_map);
v.folder_id = maybe_gen_id_opt::<Folder>(&ctx, v.folder_id, &mut id_map);
v
})
.collect();
info!("Importing data");
let upserted = window.with_tx(|tx| {
tx.batch_upsert(
workspaces,
environments,
folders,
http_requests,
grpc_requests,
websocket_requests,
&UpdateSource::Import,
)
})?;
Ok(upserted)
Ok(import::import_data(ImportDataParams {
query_manager: &query_manager,
plugin_manager: &plugin_manager,
plugin_context: &plugin_context,
workspace_context,
contents: &file,
})
.await?)
}
fn read_import_file(file_path: &str) -> Result<String> {
+10 -20
View File
@@ -14,8 +14,7 @@ use error::Result as YaakResult;
use eventsource_client::{EventParser, SSE};
use log::{debug, error, info, warn};
use std::collections::HashMap;
use std::fs::File;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::sync::Arc;
use std::time::Duration;
@@ -31,6 +30,7 @@ use tauri_plugin_window_state::{AppHandleExt, StateFlags};
use tokio::sync::Mutex;
use tokio::task::block_in_place;
use tokio::time;
use yaak::export::{self, ExportDataParams};
use yaak_common::command::new_checked_command;
use yaak_crypto::manager::EncryptionManager;
use yaak_grpc::manager::{GrpcConfig, GrpcHandle};
@@ -41,7 +41,7 @@ use yaak_models::models::{
GrpcEventType, HttpRequest, HttpResponse, HttpResponseEvent, HttpResponseState, Workspace,
WorkspaceMeta,
};
use yaak_models::util::{BatchUpsertResult, UpdateSource, get_workspace_export_resources};
use yaak_models::util::{BatchUpsertResult, UpdateSource};
use yaak_plugins::events::{
CallFolderActionArgs, CallFolderActionRequest, CallGrpcRequestActionArgs,
CallGrpcRequestActionRequest, CallHttpRequestActionArgs, CallHttpRequestActionRequest,
@@ -1384,24 +1384,14 @@ async fn cmd_export_data<R: Runtime>(
workspace_ids: Vec<&str>,
include_private_environments: bool,
) -> YaakResult<()> {
let db = app_handle.db();
let version = app_handle.package_info().version.to_string();
let export_data =
get_workspace_export_resources(&db, &version, workspace_ids, include_private_environments)?;
let f = File::options()
.create(true)
.truncate(true)
.write(true)
.open(export_path)
.expect("Unable to create file");
serde_json::to_writer_pretty(&f, &export_data)
.map_err(|e| GenericError(e.to_string()))
.expect("Failed to write");
f.sync_all().expect("Failed to sync");
Ok(())
Ok(export::export_data(ExportDataParams {
query_manager: &app_handle.db_manager(),
yaak_version: &version,
export_path: Path::new(export_path),
workspace_ids,
include_private_environments,
})?)
}
#[tauri::command]
+1
View File
@@ -12,6 +12,7 @@ serde_json = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true, features = ["sync", "rt"] }
yaak-http = { workspace = true }
yaak-core = { workspace = true }
yaak-crypto = { workspace = true }
yaak-models = { workspace = true }
yaak-plugins = { workspace = true }
+12
View File
@@ -4,6 +4,18 @@ use thiserror::Error;
pub enum Error {
#[error(transparent)]
Send(#[from] crate::send::SendHttpRequestError),
#[error(transparent)]
Model(#[from] yaak_models::error::Error),
#[error(transparent)]
Plugin(#[from] yaak_plugins::error::Error),
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
}
pub type Result<T> = std::result::Result<T, Error>;
+29
View File
@@ -0,0 +1,29 @@
use crate::Result;
use std::fs::File;
use std::path::Path;
use yaak_models::query_manager::QueryManager;
use yaak_models::util::get_workspace_export_resources;
pub struct ExportDataParams<'a> {
pub query_manager: &'a QueryManager,
pub yaak_version: &'a str,
pub export_path: &'a Path,
pub workspace_ids: Vec<&'a str>,
pub include_private_environments: bool,
}
pub fn export_data(params: ExportDataParams<'_>) -> Result<()> {
let db = params.query_manager.connect();
let export_data = get_workspace_export_resources(
&db,
params.yaak_version,
params.workspace_ids,
params.include_private_environments,
)?;
let file = File::options().create(true).truncate(true).write(true).open(params.export_path)?;
serde_json::to_writer_pretty(&file, &export_data)?;
file.sync_all()?;
Ok(())
}
+129
View File
@@ -0,0 +1,129 @@
use crate::Result;
use log::info;
use std::collections::BTreeMap;
use yaak_core::WorkspaceContext;
use yaak_models::models::{
Environment, Folder, GrpcRequest, HttpRequest, WebsocketRequest, Workspace,
};
use yaak_models::query_manager::QueryManager;
use yaak_models::util::{BatchUpsertResult, UpdateSource, maybe_gen_id, maybe_gen_id_opt};
use yaak_plugins::events::{ImportResources, PluginContext};
use yaak_plugins::manager::PluginManager;
pub struct ImportDataParams<'a> {
pub query_manager: &'a QueryManager,
pub plugin_manager: &'a PluginManager,
pub plugin_context: &'a PluginContext,
pub workspace_context: WorkspaceContext,
pub contents: &'a str,
}
pub async fn import_data(params: ImportDataParams<'_>) -> Result<BatchUpsertResult> {
let import_result =
params.plugin_manager.import_data(params.plugin_context, params.contents).await?;
import_resources(params.query_manager, params.workspace_context, import_result.resources)
}
pub fn import_resources(
query_manager: &QueryManager,
workspace_context: WorkspaceContext,
resources: ImportResources,
) -> Result<BatchUpsertResult> {
let mut id_map: BTreeMap<String, String> = BTreeMap::new();
let workspaces: Vec<Workspace> = resources
.workspaces
.into_iter()
.map(|mut v| {
v.id = maybe_gen_id::<Workspace>(&workspace_context, v.id.as_str(), &mut id_map);
v
})
.collect();
let environments: Vec<Environment> = resources
.environments
.into_iter()
.map(|mut v| {
v.id = maybe_gen_id::<Environment>(&workspace_context, v.id.as_str(), &mut id_map);
v.workspace_id =
maybe_gen_id::<Workspace>(&workspace_context, v.workspace_id.as_str(), &mut id_map);
match (v.parent_model.as_str(), v.parent_id.clone().as_deref()) {
("folder", Some(parent_id)) => {
v.parent_id =
Some(maybe_gen_id::<Folder>(&workspace_context, parent_id, &mut id_map));
}
("", _) => {
v.parent_model = "workspace".to_string();
}
_ => {
v.parent_id = None;
}
};
v
})
.collect();
let folders: Vec<Folder> = resources
.folders
.into_iter()
.map(|mut v| {
v.id = maybe_gen_id::<Folder>(&workspace_context, v.id.as_str(), &mut id_map);
v.workspace_id =
maybe_gen_id::<Workspace>(&workspace_context, v.workspace_id.as_str(), &mut id_map);
v.folder_id = maybe_gen_id_opt::<Folder>(&workspace_context, v.folder_id, &mut id_map);
v
})
.collect();
let http_requests: Vec<HttpRequest> = resources
.http_requests
.into_iter()
.map(|mut v| {
v.id = maybe_gen_id::<HttpRequest>(&workspace_context, v.id.as_str(), &mut id_map);
v.workspace_id =
maybe_gen_id::<Workspace>(&workspace_context, v.workspace_id.as_str(), &mut id_map);
v.folder_id = maybe_gen_id_opt::<Folder>(&workspace_context, v.folder_id, &mut id_map);
v
})
.collect();
let grpc_requests: Vec<GrpcRequest> = resources
.grpc_requests
.into_iter()
.map(|mut v| {
v.id = maybe_gen_id::<GrpcRequest>(&workspace_context, v.id.as_str(), &mut id_map);
v.workspace_id =
maybe_gen_id::<Workspace>(&workspace_context, v.workspace_id.as_str(), &mut id_map);
v.folder_id = maybe_gen_id_opt::<Folder>(&workspace_context, v.folder_id, &mut id_map);
v
})
.collect();
let websocket_requests: Vec<WebsocketRequest> = resources
.websocket_requests
.into_iter()
.map(|mut v| {
v.id = maybe_gen_id::<WebsocketRequest>(&workspace_context, v.id.as_str(), &mut id_map);
v.workspace_id =
maybe_gen_id::<Workspace>(&workspace_context, v.workspace_id.as_str(), &mut id_map);
v.folder_id = maybe_gen_id_opt::<Folder>(&workspace_context, v.folder_id, &mut id_map);
v
})
.collect();
info!("Importing data");
query_manager.with_tx(|tx| {
tx.batch_upsert(
workspaces,
environments,
folders,
http_requests,
grpc_requests,
websocket_requests,
&UpdateSource::Import,
)
.map_err(crate::Error::from)
})
}
+2
View File
@@ -1,4 +1,6 @@
pub mod error;
pub mod export;
pub mod import;
pub mod plugin_events;
pub mod render;
pub mod send;