mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-02-27 12:20:24 -05:00
Compare commits
2 Commits
codex/cli-
...
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())?;
|
window.db().resolve_environments(&workspace.id, folder_id, environment_id.as_deref())?;
|
||||||
let request = render_http_request(&resolved, env_chain, &cb, &RenderOptions::throw()).await?;
|
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
|
// Build the sendable request using the new SendableHttpRequest type
|
||||||
let options = SendableHttpRequestOptions {
|
let options = SendableHttpRequestOptions {
|
||||||
follow_redirects: workspace.setting_follow_redirects,
|
follow_redirects: resolved_settings.follow_redirects,
|
||||||
timeout: if workspace.setting_request_timeout > 0 {
|
timeout: if resolved_settings.request_timeout > 0 {
|
||||||
Some(Duration::from_millis(workspace.setting_request_timeout.unsigned_abs() as u64))
|
Some(Duration::from_millis(resolved_settings.request_timeout.unsigned_abs() as u64))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
@@ -231,7 +234,7 @@ async fn send_http_request_inner<R: Runtime>(
|
|||||||
let client = connection_manager
|
let client = connection_manager
|
||||||
.get_client(&HttpConnectionOptions {
|
.get_client(&HttpConnectionOptions {
|
||||||
id: plugin_context.id.clone(),
|
id: plugin_context.id.clone(),
|
||||||
validate_certificates: workspace.setting_validate_certificates,
|
validate_certificates: resolved_settings.validate_certificates,
|
||||||
proxy: proxy_setting,
|
proxy: proxy_setting,
|
||||||
client_certificate,
|
client_certificate,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -233,7 +233,7 @@ async fn cmd_grpc_reflect<R: Runtime>(
|
|||||||
&uri,
|
&uri,
|
||||||
&proto_files.iter().map(|p| PathBuf::from_str(p).unwrap()).collect(),
|
&proto_files.iter().map(|p| PathBuf::from_str(p).unwrap()).collect(),
|
||||||
&metadata,
|
&metadata,
|
||||||
workspace.setting_validate_certificates,
|
workspace.setting_validate_certificates.unwrap_or(true),
|
||||||
client_certificate,
|
client_certificate,
|
||||||
skip_cache.unwrap_or(false),
|
skip_cache.unwrap_or(false),
|
||||||
)
|
)
|
||||||
@@ -327,7 +327,7 @@ async fn cmd_grpc_go<R: Runtime>(
|
|||||||
uri.as_str(),
|
uri.as_str(),
|
||||||
&proto_files.iter().map(|p| PathBuf::from_str(p).unwrap()).collect(),
|
&proto_files.iter().map(|p| PathBuf::from_str(p).unwrap()).collect(),
|
||||||
&metadata,
|
&metadata,
|
||||||
workspace.setting_validate_certificates,
|
workspace.setting_validate_certificates.unwrap_or(true),
|
||||||
client_cert.clone(),
|
client_cert.clone(),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|||||||
@@ -355,7 +355,7 @@ pub async fn cmd_ws_connect<R: Runtime>(
|
|||||||
url.as_str(),
|
url.as_str(),
|
||||||
headers,
|
headers,
|
||||||
receive_tx,
|
receive_tx,
|
||||||
workspace.setting_validate_certificates,
|
workspace.setting_validate_certificates.unwrap_or(true),
|
||||||
client_cert,
|
client_cert,
|
||||||
)
|
)
|
||||||
.await
|
.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::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 crate::util::{UpdateSource, generate_prefixed_id};
|
||||||
use chrono::{NaiveDateTime, Utc};
|
use chrono::{NaiveDateTime, Utc};
|
||||||
use rusqlite::Row;
|
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)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "gen_models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
@@ -297,12 +323,10 @@ pub struct Workspace {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
pub encryption_key_challenge: Option<String>,
|
pub encryption_key_challenge: Option<String>,
|
||||||
|
|
||||||
// Settings
|
// Inheritable settings (Option = can be null, defaults applied at resolution time)
|
||||||
#[serde(default = "default_true")]
|
pub setting_validate_certificates: Option<bool>,
|
||||||
pub setting_validate_certificates: bool,
|
pub setting_follow_redirects: Option<bool>,
|
||||||
#[serde(default = "default_true")]
|
pub setting_request_timeout: Option<i32>,
|
||||||
pub setting_follow_redirects: bool,
|
|
||||||
pub setting_request_timeout: i32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UpsertModelInfo for Workspace {
|
impl UpsertModelInfo for Workspace {
|
||||||
@@ -726,6 +750,11 @@ pub struct Folder {
|
|||||||
pub headers: Vec<HttpRequestHeader>,
|
pub headers: Vec<HttpRequestHeader>,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub sort_priority: f64,
|
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 {
|
impl UpsertModelInfo for Folder {
|
||||||
@@ -765,6 +794,9 @@ impl UpsertModelInfo for Folder {
|
|||||||
(Description, self.description.into()),
|
(Description, self.description.into()),
|
||||||
(Name, self.name.trim().into()),
|
(Name, self.name.trim().into()),
|
||||||
(SortPriority, self.sort_priority.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::Description,
|
||||||
FolderIden::FolderId,
|
FolderIden::FolderId,
|
||||||
FolderIden::SortPriority,
|
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(),
|
headers: serde_json::from_str(&headers).unwrap_or_default(),
|
||||||
authentication_type: row.get("authentication_type")?,
|
authentication_type: row.get("authentication_type")?,
|
||||||
authentication: serde_json::from_str(&authentication).unwrap_or_default(),
|
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 sort_priority: f64,
|
||||||
pub url: String,
|
pub url: String,
|
||||||
pub url_parameters: Vec<HttpUrlParameter>,
|
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 {
|
impl UpsertModelInfo for HttpRequest {
|
||||||
@@ -884,6 +927,7 @@ impl UpsertModelInfo for HttpRequest {
|
|||||||
self,
|
self,
|
||||||
source: &UpdateSource,
|
source: &UpdateSource,
|
||||||
) -> Result<Vec<(impl IntoIden + Eq, impl Into<SimpleExpr>)>> {
|
) -> Result<Vec<(impl IntoIden + Eq, impl Into<SimpleExpr>)>> {
|
||||||
|
use HttpRequestIden::*;
|
||||||
Ok(vec![
|
Ok(vec![
|
||||||
(CreatedAt, upsert_date(source, self.created_at)),
|
(CreatedAt, upsert_date(source, self.created_at)),
|
||||||
(UpdatedAt, upsert_date(source, self.updated_at)),
|
(UpdatedAt, upsert_date(source, self.updated_at)),
|
||||||
@@ -900,10 +944,14 @@ impl UpsertModelInfo for HttpRequest {
|
|||||||
(AuthenticationType, self.authentication_type.into()),
|
(AuthenticationType, self.authentication_type.into()),
|
||||||
(Headers, serde_json::to_string(&self.headers)?.into()),
|
(Headers, serde_json::to_string(&self.headers)?.into()),
|
||||||
(SortPriority, self.sort_priority.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> {
|
fn update_columns() -> Vec<impl IntoIden> {
|
||||||
|
use HttpRequestIden::*;
|
||||||
vec![
|
vec![
|
||||||
UpdatedAt,
|
UpdatedAt,
|
||||||
WorkspaceId,
|
WorkspaceId,
|
||||||
@@ -919,6 +967,9 @@ impl UpsertModelInfo for HttpRequest {
|
|||||||
Url,
|
Url,
|
||||||
UrlParameters,
|
UrlParameters,
|
||||||
SortPriority,
|
SortPriority,
|
||||||
|
SettingValidateCertificates,
|
||||||
|
SettingFollowRedirects,
|
||||||
|
SettingRequestTimeout,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -945,6 +996,9 @@ impl UpsertModelInfo for HttpRequest {
|
|||||||
sort_priority: row.get("sort_priority")?,
|
sort_priority: row.get("sort_priority")?,
|
||||||
url: row.get("url")?,
|
url: row.get("url")?,
|
||||||
url_parameters: serde_json::from_str(url_parameters.as_str()).unwrap_or_default(),
|
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::db_context::DbContext;
|
||||||
use crate::error::Result;
|
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 crate::util::UpdateSource;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
@@ -103,4 +103,79 @@ impl<'a> DbContext<'a> {
|
|||||||
}
|
}
|
||||||
Ok(children)
|
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(
|
workspaces.push(self.upsert_workspace(
|
||||||
&Workspace {
|
&Workspace {
|
||||||
name: "Yaak".to_string(),
|
name: "Yaak".to_string(),
|
||||||
setting_follow_redirects: true,
|
setting_follow_redirects: Some(true),
|
||||||
setting_validate_certificates: true,
|
setting_validate_certificates: Some(true),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
&UpdateSource::Background,
|
&UpdateSource::Background,
|
||||||
|
|||||||
Reference in New Issue
Block a user