mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-01-11 20:00:29 +01:00
Compare commits
2 Commits
main
...
omnara/pre
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a52032988 | ||
|
|
4b7497a908 |
@@ -178,11 +178,14 @@ async fn send_http_request_inner<R: Runtime>(
|
||||
window.db().resolve_environments(&workspace.id, folder_id, environment_id.as_deref())?;
|
||||
let request = render_http_request(&resolved, env_chain, &cb, &RenderOptions::throw()).await?;
|
||||
|
||||
// Resolve inherited settings for this request
|
||||
let resolved_settings = window.db().resolve_settings_for_http_request(&resolved)?;
|
||||
|
||||
// Build the sendable request using the new SendableHttpRequest type
|
||||
let options = SendableHttpRequestOptions {
|
||||
follow_redirects: workspace.setting_follow_redirects,
|
||||
timeout: if workspace.setting_request_timeout > 0 {
|
||||
Some(Duration::from_millis(workspace.setting_request_timeout.unsigned_abs() as u64))
|
||||
follow_redirects: resolved_settings.follow_redirects,
|
||||
timeout: if resolved_settings.request_timeout > 0 {
|
||||
Some(Duration::from_millis(resolved_settings.request_timeout.unsigned_abs() as u64))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
@@ -231,7 +234,7 @@ async fn send_http_request_inner<R: Runtime>(
|
||||
let client = connection_manager
|
||||
.get_client(&HttpConnectionOptions {
|
||||
id: plugin_context.id.clone(),
|
||||
validate_certificates: workspace.setting_validate_certificates,
|
||||
validate_certificates: resolved_settings.validate_certificates,
|
||||
proxy: proxy_setting,
|
||||
client_certificate,
|
||||
})
|
||||
|
||||
@@ -233,7 +233,7 @@ async fn cmd_grpc_reflect<R: Runtime>(
|
||||
&uri,
|
||||
&proto_files.iter().map(|p| PathBuf::from_str(p).unwrap()).collect(),
|
||||
&metadata,
|
||||
workspace.setting_validate_certificates,
|
||||
workspace.setting_validate_certificates.unwrap_or(true),
|
||||
client_certificate,
|
||||
skip_cache.unwrap_or(false),
|
||||
)
|
||||
@@ -327,7 +327,7 @@ async fn cmd_grpc_go<R: Runtime>(
|
||||
uri.as_str(),
|
||||
&proto_files.iter().map(|p| PathBuf::from_str(p).unwrap()).collect(),
|
||||
&metadata,
|
||||
workspace.setting_validate_certificates,
|
||||
workspace.setting_validate_certificates.unwrap_or(true),
|
||||
client_cert.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -355,7 +355,7 @@ pub async fn cmd_ws_connect<R: Runtime>(
|
||||
url.as_str(),
|
||||
headers,
|
||||
receive_tx,
|
||||
workspace.setting_validate_certificates,
|
||||
workspace.setting_validate_certificates.unwrap_or(true),
|
||||
client_cert,
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
-- Add nullable settings columns to folders (NULL = inherit from parent)
|
||||
ALTER TABLE folders ADD COLUMN setting_request_timeout INTEGER DEFAULT NULL;
|
||||
ALTER TABLE folders ADD COLUMN setting_validate_certificates BOOLEAN DEFAULT NULL;
|
||||
ALTER TABLE folders ADD COLUMN setting_follow_redirects BOOLEAN DEFAULT NULL;
|
||||
|
||||
-- Add nullable settings columns to http_requests (NULL = inherit from parent)
|
||||
ALTER TABLE http_requests ADD COLUMN setting_request_timeout INTEGER DEFAULT NULL;
|
||||
ALTER TABLE http_requests ADD COLUMN setting_validate_certificates BOOLEAN DEFAULT NULL;
|
||||
ALTER TABLE http_requests ADD COLUMN setting_follow_redirects BOOLEAN DEFAULT NULL;
|
||||
@@ -1,8 +1,4 @@
|
||||
use crate::error::Result;
|
||||
use crate::models::HttpRequestIden::{
|
||||
Authentication, AuthenticationType, Body, BodyType, CreatedAt, Description, FolderId, Headers,
|
||||
Method, Name, SortPriority, UpdatedAt, Url, UrlParameters, WorkspaceId,
|
||||
};
|
||||
use crate::util::{UpdateSource, generate_prefixed_id};
|
||||
use chrono::{NaiveDateTime, Utc};
|
||||
use rusqlite::Row;
|
||||
@@ -115,6 +111,36 @@ impl Default for EditorKeymap {
|
||||
}
|
||||
}
|
||||
|
||||
/// Settings that can be inherited at workspace → folder → request level.
|
||||
/// All fields optional - None means "inherit from parent" (or use default if at root).
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct HttpRequestSettingsOverride {
|
||||
pub setting_validate_certificates: Option<bool>,
|
||||
pub setting_follow_redirects: Option<bool>,
|
||||
pub setting_request_timeout: Option<i32>,
|
||||
}
|
||||
|
||||
/// Resolved settings with concrete values (after inheritance + defaults applied)
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ResolvedHttpRequestSettings {
|
||||
pub validate_certificates: bool,
|
||||
pub follow_redirects: bool,
|
||||
pub request_timeout: i32,
|
||||
}
|
||||
|
||||
impl ResolvedHttpRequestSettings {
|
||||
/// Default values when nothing is set in the inheritance chain
|
||||
pub fn defaults() -> Self {
|
||||
Self {
|
||||
validate_certificates: true,
|
||||
follow_redirects: true,
|
||||
request_timeout: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
@@ -297,12 +323,10 @@ pub struct Workspace {
|
||||
pub name: String,
|
||||
pub encryption_key_challenge: Option<String>,
|
||||
|
||||
// Settings
|
||||
#[serde(default = "default_true")]
|
||||
pub setting_validate_certificates: bool,
|
||||
#[serde(default = "default_true")]
|
||||
pub setting_follow_redirects: bool,
|
||||
pub setting_request_timeout: i32,
|
||||
// Inheritable settings (Option = can be null, defaults applied at resolution time)
|
||||
pub setting_validate_certificates: Option<bool>,
|
||||
pub setting_follow_redirects: Option<bool>,
|
||||
pub setting_request_timeout: Option<i32>,
|
||||
}
|
||||
|
||||
impl UpsertModelInfo for Workspace {
|
||||
@@ -726,6 +750,11 @@ pub struct Folder {
|
||||
pub headers: Vec<HttpRequestHeader>,
|
||||
pub name: String,
|
||||
pub sort_priority: f64,
|
||||
|
||||
// Inheritable settings (Option = null means inherit from parent)
|
||||
pub setting_validate_certificates: Option<bool>,
|
||||
pub setting_follow_redirects: Option<bool>,
|
||||
pub setting_request_timeout: Option<i32>,
|
||||
}
|
||||
|
||||
impl UpsertModelInfo for Folder {
|
||||
@@ -765,6 +794,9 @@ impl UpsertModelInfo for Folder {
|
||||
(Description, self.description.into()),
|
||||
(Name, self.name.trim().into()),
|
||||
(SortPriority, self.sort_priority.into()),
|
||||
(SettingValidateCertificates, self.setting_validate_certificates.into()),
|
||||
(SettingFollowRedirects, self.setting_follow_redirects.into()),
|
||||
(SettingRequestTimeout, self.setting_request_timeout.into()),
|
||||
])
|
||||
}
|
||||
|
||||
@@ -778,6 +810,9 @@ impl UpsertModelInfo for Folder {
|
||||
FolderIden::Description,
|
||||
FolderIden::FolderId,
|
||||
FolderIden::SortPriority,
|
||||
FolderIden::SettingValidateCertificates,
|
||||
FolderIden::SettingFollowRedirects,
|
||||
FolderIden::SettingRequestTimeout,
|
||||
]
|
||||
}
|
||||
|
||||
@@ -800,6 +835,9 @@ impl UpsertModelInfo for Folder {
|
||||
headers: serde_json::from_str(&headers).unwrap_or_default(),
|
||||
authentication_type: row.get("authentication_type")?,
|
||||
authentication: serde_json::from_str(&authentication).unwrap_or_default(),
|
||||
setting_validate_certificates: row.get("setting_validate_certificates")?,
|
||||
setting_follow_redirects: row.get("setting_follow_redirects")?,
|
||||
setting_request_timeout: row.get("setting_request_timeout")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -857,6 +895,11 @@ pub struct HttpRequest {
|
||||
pub sort_priority: f64,
|
||||
pub url: String,
|
||||
pub url_parameters: Vec<HttpUrlParameter>,
|
||||
|
||||
// Inheritable settings (Option = null means inherit from parent)
|
||||
pub setting_validate_certificates: Option<bool>,
|
||||
pub setting_follow_redirects: Option<bool>,
|
||||
pub setting_request_timeout: Option<i32>,
|
||||
}
|
||||
|
||||
impl UpsertModelInfo for HttpRequest {
|
||||
@@ -884,6 +927,7 @@ impl UpsertModelInfo for HttpRequest {
|
||||
self,
|
||||
source: &UpdateSource,
|
||||
) -> Result<Vec<(impl IntoIden + Eq, impl Into<SimpleExpr>)>> {
|
||||
use HttpRequestIden::*;
|
||||
Ok(vec![
|
||||
(CreatedAt, upsert_date(source, self.created_at)),
|
||||
(UpdatedAt, upsert_date(source, self.updated_at)),
|
||||
@@ -900,10 +944,14 @@ impl UpsertModelInfo for HttpRequest {
|
||||
(AuthenticationType, self.authentication_type.into()),
|
||||
(Headers, serde_json::to_string(&self.headers)?.into()),
|
||||
(SortPriority, self.sort_priority.into()),
|
||||
(SettingValidateCertificates, self.setting_validate_certificates.into()),
|
||||
(SettingFollowRedirects, self.setting_follow_redirects.into()),
|
||||
(SettingRequestTimeout, self.setting_request_timeout.into()),
|
||||
])
|
||||
}
|
||||
|
||||
fn update_columns() -> Vec<impl IntoIden> {
|
||||
use HttpRequestIden::*;
|
||||
vec![
|
||||
UpdatedAt,
|
||||
WorkspaceId,
|
||||
@@ -919,6 +967,9 @@ impl UpsertModelInfo for HttpRequest {
|
||||
Url,
|
||||
UrlParameters,
|
||||
SortPriority,
|
||||
SettingValidateCertificates,
|
||||
SettingFollowRedirects,
|
||||
SettingRequestTimeout,
|
||||
]
|
||||
}
|
||||
|
||||
@@ -945,6 +996,9 @@ impl UpsertModelInfo for HttpRequest {
|
||||
sort_priority: row.get("sort_priority")?,
|
||||
url: row.get("url")?,
|
||||
url_parameters: serde_json::from_str(url_parameters.as_str()).unwrap_or_default(),
|
||||
setting_validate_certificates: row.get("setting_validate_certificates")?,
|
||||
setting_follow_redirects: row.get("setting_follow_redirects")?,
|
||||
setting_request_timeout: row.get("setting_request_timeout")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::db_context::DbContext;
|
||||
use crate::error::Result;
|
||||
use crate::models::{Folder, FolderIden, HttpRequest, HttpRequestHeader, HttpRequestIden};
|
||||
use crate::models::{Folder, FolderIden, HttpRequest, HttpRequestHeader, HttpRequestIden, ResolvedHttpRequestSettings};
|
||||
use crate::util::UpdateSource;
|
||||
use serde_json::Value;
|
||||
use std::collections::BTreeMap;
|
||||
@@ -103,4 +103,79 @@ impl<'a> DbContext<'a> {
|
||||
}
|
||||
Ok(children)
|
||||
}
|
||||
|
||||
/// Resolve settings for an HTTP request by walking the inheritance chain:
|
||||
/// Workspace → Folder(s) → Request
|
||||
/// Last non-None value wins, then defaults are applied.
|
||||
pub fn resolve_settings_for_http_request(
|
||||
&self,
|
||||
http_request: &HttpRequest,
|
||||
) -> Result<ResolvedHttpRequestSettings> {
|
||||
let workspace = self.get_workspace(&http_request.workspace_id)?;
|
||||
|
||||
// Start with None for all settings
|
||||
let mut validate_certs: Option<bool> = None;
|
||||
let mut follow_redirects: Option<bool> = None;
|
||||
let mut timeout: Option<i32> = None;
|
||||
|
||||
// Apply workspace settings
|
||||
if workspace.setting_validate_certificates.is_some() {
|
||||
validate_certs = workspace.setting_validate_certificates;
|
||||
}
|
||||
if workspace.setting_follow_redirects.is_some() {
|
||||
follow_redirects = workspace.setting_follow_redirects;
|
||||
}
|
||||
if workspace.setting_request_timeout.is_some() {
|
||||
timeout = workspace.setting_request_timeout;
|
||||
}
|
||||
|
||||
// Apply folder chain settings (root first, immediate parent last)
|
||||
if let Some(folder_id) = &http_request.folder_id {
|
||||
let folders = self.get_folder_ancestors(folder_id)?;
|
||||
for folder in folders {
|
||||
if folder.setting_validate_certificates.is_some() {
|
||||
validate_certs = folder.setting_validate_certificates;
|
||||
}
|
||||
if folder.setting_follow_redirects.is_some() {
|
||||
follow_redirects = folder.setting_follow_redirects;
|
||||
}
|
||||
if folder.setting_request_timeout.is_some() {
|
||||
timeout = folder.setting_request_timeout;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply request-level settings (highest priority)
|
||||
if http_request.setting_validate_certificates.is_some() {
|
||||
validate_certs = http_request.setting_validate_certificates;
|
||||
}
|
||||
if http_request.setting_follow_redirects.is_some() {
|
||||
follow_redirects = http_request.setting_follow_redirects;
|
||||
}
|
||||
if http_request.setting_request_timeout.is_some() {
|
||||
timeout = http_request.setting_request_timeout;
|
||||
}
|
||||
|
||||
// Apply defaults for anything still None
|
||||
Ok(ResolvedHttpRequestSettings {
|
||||
validate_certificates: validate_certs.unwrap_or(true),
|
||||
follow_redirects: follow_redirects.unwrap_or(true),
|
||||
request_timeout: timeout.unwrap_or(0),
|
||||
})
|
||||
}
|
||||
|
||||
/// Get folder ancestors in order from root to immediate parent
|
||||
fn get_folder_ancestors(&self, folder_id: &str) -> Result<Vec<Folder>> {
|
||||
let mut ancestors = Vec::new();
|
||||
let mut current_id = Some(folder_id.to_string());
|
||||
|
||||
while let Some(id) = current_id {
|
||||
let folder = self.get_folder(&id)?;
|
||||
current_id = folder.folder_id.clone();
|
||||
ancestors.push(folder);
|
||||
}
|
||||
|
||||
ancestors.reverse(); // Root first, immediate parent last
|
||||
Ok(ancestors)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ impl<'a> DbContext<'a> {
|
||||
workspaces.push(self.upsert_workspace(
|
||||
&Workspace {
|
||||
name: "Yaak".to_string(),
|
||||
setting_follow_redirects: true,
|
||||
setting_validate_certificates: true,
|
||||
setting_follow_redirects: Some(true),
|
||||
setting_validate_certificates: Some(true),
|
||||
..Default::default()
|
||||
},
|
||||
&UpdateSource::Background,
|
||||
|
||||
Reference in New Issue
Block a user