mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-08-26 05:14:11 +02:00
155 lines
5.2 KiB
Rust
155 lines
5.2 KiB
Rust
use crate::PluginContextExt;
|
|
use crate::error::{Error, Result};
|
|
use crate::models_ext::QueryManagerExt;
|
|
use std::fs::read_to_string;
|
|
use std::io::ErrorKind;
|
|
use tauri::{Manager, Runtime, WebviewWindow};
|
|
use yaak::import::{self, ImportDataParams};
|
|
use yaak_api::{ApiClientKind, yaak_api_client};
|
|
use yaak_core::WorkspaceContext;
|
|
use yaak_models::util::BatchUpsertResult;
|
|
use yaak_plugins::manager::PluginManager;
|
|
use yaak_tauri_utils::window::WorkspaceWindowTrait;
|
|
|
|
pub(crate) async fn import_data<R: Runtime>(
|
|
window: &WebviewWindow<R>,
|
|
file_path: &str,
|
|
) -> Result<BatchUpsertResult> {
|
|
let contents = read_import_file(file_path)?;
|
|
import_contents(window, &contents).await
|
|
}
|
|
|
|
pub(crate) async fn import_url<R: Runtime>(
|
|
window: &WebviewWindow<R>,
|
|
url: &str,
|
|
) -> Result<BatchUpsertResult> {
|
|
let contents = fetch_import_url(window, url).await?;
|
|
import_contents(window, &contents).await
|
|
}
|
|
|
|
async fn import_contents<R: Runtime>(
|
|
window: &WebviewWindow<R>,
|
|
contents: &str,
|
|
) -> Result<BatchUpsertResult> {
|
|
let plugin_manager = window.state::<PluginManager>();
|
|
let query_manager = window.db_manager();
|
|
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,
|
|
};
|
|
|
|
Ok(import::import_data(ImportDataParams {
|
|
query_manager: &query_manager,
|
|
plugin_manager: &plugin_manager,
|
|
plugin_context: &plugin_context,
|
|
workspace_context,
|
|
contents,
|
|
})
|
|
.await?)
|
|
}
|
|
|
|
/// Download an importable document (OpenAPI, Postman, Insomnia, …) so it can be fed to the same
|
|
/// pipeline as a file on disk.
|
|
///
|
|
/// This uses Yaak's own API client, which follows the OS proxy but not the workspace's proxy,
|
|
/// client certificate, or certificate-validation settings. Requests are unauthenticated, so
|
|
/// specs behind auth must still be downloaded manually and imported as a file.
|
|
async fn fetch_import_url<R: Runtime>(window: &WebviewWindow<R>, url: &str) -> Result<String> {
|
|
let url = normalize_import_url(url)?;
|
|
let app_version = window.app_handle().package_info().version.to_string();
|
|
let response = yaak_api_client(ApiClientKind::App, &app_version)?
|
|
.get(&url)
|
|
// The API client defaults to JSON, but specs are just as often YAML
|
|
.header("Accept", "*/*")
|
|
.send()
|
|
.await
|
|
.map_err(|err| Error::GenericError(format!("Failed to fetch {url}: {err}")))?;
|
|
|
|
let status = response.status();
|
|
if !status.is_success() {
|
|
return Err(Error::GenericError(format!("Failed to fetch {url}: responded with {status}")));
|
|
}
|
|
|
|
response
|
|
.text()
|
|
.await
|
|
.map_err(|err| Error::GenericError(format!("Failed to read response from {url}: {err}")))
|
|
}
|
|
|
|
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()));
|
|
}
|
|
|
|
if url.starts_with("http://") || url.starts_with("https://") {
|
|
return Ok(url.to_string());
|
|
}
|
|
|
|
match url.split_once("://") {
|
|
Some((scheme, _)) => {
|
|
Err(Error::GenericError(format!("Import URL must be http or https, but got {scheme}")))
|
|
}
|
|
None => Ok(format!("https://{url}")),
|
|
}
|
|
}
|
|
|
|
fn read_import_file(file_path: &str) -> Result<String> {
|
|
read_to_string(file_path).map_err(|err| {
|
|
if err.kind() == ErrorKind::InvalidData {
|
|
Error::GenericError(format!(
|
|
"Import file must be UTF-8 text; binary files are not supported: {file_path}"
|
|
))
|
|
} else {
|
|
Error::GenericError(format!("Unable to read import file {file_path}: {err}"))
|
|
}
|
|
})
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use std::fs::{remove_file, write};
|
|
use std::time::{SystemTime, UNIX_EPOCH};
|
|
|
|
#[test]
|
|
fn read_import_file_returns_error_for_binary_file() {
|
|
let path = std::env::temp_dir().join(format!(
|
|
"yaak-import-binary-{}.pftrace",
|
|
SystemTime::now()
|
|
.duration_since(UNIX_EPOCH)
|
|
.expect("system time before unix epoch")
|
|
.as_nanos()
|
|
));
|
|
write(&path, [0xff, 0xfe, 0xfd]).expect("write binary fixture");
|
|
|
|
let err = read_import_file(path.to_str().expect("temp path is utf-8"))
|
|
.expect_err("binary import should return an error");
|
|
|
|
assert!(err.to_string().contains("binary files are not supported"));
|
|
|
|
remove_file(path).expect("remove binary fixture");
|
|
}
|
|
|
|
#[test]
|
|
fn normalize_import_url_defaults_to_https() {
|
|
assert_eq!(
|
|
normalize_import_url(" example.com/openapi.yaml ").unwrap(),
|
|
"https://example.com/openapi.yaml"
|
|
);
|
|
assert_eq!(
|
|
normalize_import_url("http://example.com/openapi.yaml").unwrap(),
|
|
"http://example.com/openapi.yaml"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn normalize_import_url_rejects_other_schemes() {
|
|
assert!(normalize_import_url("file:///tmp/openapi.yaml").is_err());
|
|
assert!(normalize_import_url(" ").is_err());
|
|
}
|
|
}
|