Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions crates-tauri/yaak-app/src/http_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
},
Expand Down Expand Up @@ -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,
})
Expand Down
4 changes: 2 additions & 2 deletions crates-tauri/yaak-app/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
)
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion crates-tauri/yaak-app/src/ws_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
74 changes: 64 additions & 10 deletions crates/yaak-models/src/models.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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")]
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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()),
])
}

Expand All @@ -778,6 +810,9 @@ impl UpsertModelInfo for Folder {
FolderIden::Description,
FolderIden::FolderId,
FolderIden::SortPriority,
FolderIden::SettingValidateCertificates,
FolderIden::SettingFollowRedirects,
FolderIden::SettingRequestTimeout,
]
}

Expand All @@ -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")?,
})
}
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)),
Expand All @@ -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,
Expand All @@ -919,6 +967,9 @@ impl UpsertModelInfo for HttpRequest {
Url,
UrlParameters,
SortPriority,
SettingValidateCertificates,
SettingFollowRedirects,
SettingRequestTimeout,
]
}

Expand All @@ -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")?,
})
}
}
Expand Down
77 changes: 76 additions & 1 deletion crates/yaak-models/src/queries/http_requests.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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)
}
}
4 changes: 2 additions & 2 deletions crates/yaak-models/src/queries/workspaces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down