mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-08-28 22:27:21 +02:00
feat(settings): add HTTP version as an inherited request setting (#609)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
6777003b70
commit
b7b8ae5f94
+16
-1
@@ -110,6 +110,7 @@ export type Folder = {
|
||||
settingFollowRedirects: InheritedBoolSetting;
|
||||
settingRequestTimeout: InheritedIntSetting;
|
||||
settingRequestMessageSize: InheritedIntSetting;
|
||||
settingHttpVersion: InheritedHttpVersionSetting;
|
||||
};
|
||||
|
||||
export type GraphQlIntrospection = {
|
||||
@@ -214,6 +215,7 @@ export type HttpRequest = {
|
||||
settingValidateCertificates: InheritedBoolSetting;
|
||||
settingFollowRedirects: InheritedBoolSetting;
|
||||
settingRequestTimeout: InheritedIntSetting;
|
||||
settingHttpVersion: InheritedHttpVersionSetting;
|
||||
};
|
||||
|
||||
export type HttpRequestHeader = { enabled?: boolean; name: string; value: string; id?: string };
|
||||
@@ -318,6 +320,7 @@ export type HttpSendSettings = {
|
||||
timeoutMs: number;
|
||||
sendCookies: boolean;
|
||||
storeCookies: boolean;
|
||||
httpVersion: HttpVersion;
|
||||
};
|
||||
|
||||
export type HttpUrlParameter = {
|
||||
@@ -331,8 +334,12 @@ export type HttpUrlParameter = {
|
||||
id?: string;
|
||||
};
|
||||
|
||||
export type HttpVersion = "auto" | "http1" | "http2";
|
||||
|
||||
export type InheritedBoolSetting = { enabled?: boolean; value: boolean };
|
||||
|
||||
export type InheritedHttpVersionSetting = { enabled?: boolean; value: HttpVersion };
|
||||
|
||||
export type InheritedIntSetting = { enabled?: boolean; value: number };
|
||||
|
||||
export type KeyValue = {
|
||||
@@ -475,7 +482,14 @@ export type WebsocketEvent = {
|
||||
};
|
||||
|
||||
export type WebsocketEventType =
|
||||
"binary" | "close" | "error" | "frame" | "open" | "ping" | "pong" | "text";
|
||||
| "binary"
|
||||
| "close"
|
||||
| "error"
|
||||
| "frame"
|
||||
| "open"
|
||||
| "ping"
|
||||
| "pong"
|
||||
| "text";
|
||||
|
||||
export type WebsocketMessageType = "text" | "binary";
|
||||
|
||||
@@ -522,6 +536,7 @@ export type Workspace = {
|
||||
settingDnsOverrides: Array<DnsOverride>;
|
||||
settingSendCookies: boolean;
|
||||
settingStoreCookies: boolean;
|
||||
settingHttpVersion: HttpVersion;
|
||||
};
|
||||
|
||||
export type WorkspaceMeta = {
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
ALTER TABLE workspaces ADD COLUMN setting_http_version TEXT DEFAULT 'auto' NOT NULL;
|
||||
|
||||
ALTER TABLE folders ADD COLUMN setting_http_version TEXT DEFAULT '{"enabled":false,"value":"auto"}' NOT NULL;
|
||||
|
||||
ALTER TABLE http_requests ADD COLUMN setting_http_version TEXT DEFAULT '{"enabled":false,"value":"auto"}' NOT NULL;
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::error::Result;
|
||||
use crate::models::HttpRequestIden::{
|
||||
Authentication, AuthenticationType, Body, BodyType, CreatedAt, Description, FolderId, Headers,
|
||||
Method, Name, SettingFollowRedirects, SettingRequestTimeout, SettingSendCookies,
|
||||
SettingStoreCookies, SettingValidateCertificates, SortPriority, UpdatedAt, Url, UrlParameters,
|
||||
WorkspaceId,
|
||||
Method, Name, SettingFollowRedirects, SettingHttpVersion, SettingRequestTimeout,
|
||||
SettingSendCookies, SettingStoreCookies, SettingValidateCertificates, SortPriority, UpdatedAt,
|
||||
Url, UrlParameters, WorkspaceId,
|
||||
};
|
||||
use crate::util::generate_prefixed_id;
|
||||
use chrono::{NaiveDateTime, Utc};
|
||||
@@ -143,6 +143,7 @@ pub struct ResolvedHttpRequestSettings {
|
||||
pub request_message_size: ResolvedSetting<i32>,
|
||||
pub send_cookies: ResolvedSetting<bool>,
|
||||
pub store_cookies: ResolvedSetting<bool>,
|
||||
pub http_version: ResolvedSetting<HttpVersion>,
|
||||
}
|
||||
|
||||
impl Default for ResolvedHttpRequestSettings {
|
||||
@@ -154,6 +155,7 @@ impl Default for ResolvedHttpRequestSettings {
|
||||
request_message_size: ResolvedSetting::default_source(DEFAULT_REQUEST_MESSAGE_SIZE),
|
||||
send_cookies: ResolvedSetting::default_source(true),
|
||||
store_cookies: ResolvedSetting::default_source(true),
|
||||
http_version: ResolvedSetting::default_source(HttpVersion::Auto),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,6 +193,7 @@ impl ResolvedHttpRequestSettings {
|
||||
event("timeout", timeout, &self.request_timeout),
|
||||
event("send_cookies", self.send_cookies.value.to_string(), &self.send_cookies),
|
||||
event("store_cookies", self.store_cookies.value.to_string(), &self.store_cookies),
|
||||
event("http_version", self.http_version.value.to_string(), &self.http_version),
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -208,6 +211,8 @@ pub struct HttpSendSettings {
|
||||
pub timeout_ms: i32,
|
||||
pub send_cookies: bool,
|
||||
pub store_cookies: bool,
|
||||
#[serde(default)]
|
||||
pub http_version: HttpVersion,
|
||||
}
|
||||
|
||||
impl From<&ResolvedHttpRequestSettings> for HttpSendSettings {
|
||||
@@ -218,6 +223,7 @@ impl From<&ResolvedHttpRequestSettings> for HttpSendSettings {
|
||||
timeout_ms: s.request_timeout.value,
|
||||
send_cookies: s.send_cookies.value,
|
||||
store_cookies: s.store_cookies.value,
|
||||
http_version: s.http_version.value,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -255,6 +261,49 @@ impl Default for InheritedIntSetting {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub enum HttpVersion {
|
||||
#[default]
|
||||
Auto,
|
||||
Http1,
|
||||
Http2,
|
||||
}
|
||||
|
||||
impl FromStr for HttpVersion {
|
||||
type Err = crate::error::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self> {
|
||||
match s {
|
||||
"http1" => Ok(Self::Http1),
|
||||
"http2" => Ok(Self::Http2),
|
||||
_ => Ok(Self::Auto),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for HttpVersion {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let str = match self {
|
||||
HttpVersion::Auto => "auto",
|
||||
HttpVersion::Http1 => "http1",
|
||||
HttpVersion::Http2 => "http2",
|
||||
};
|
||||
write!(f, "{}", str)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize, JsonSchema, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct InheritedHttpVersionSetting {
|
||||
#[serde(default)]
|
||||
#[ts(optional, as = "Option<bool>")]
|
||||
pub enabled: bool,
|
||||
pub value: HttpVersion,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
@@ -484,6 +533,7 @@ impl Default for Workspace {
|
||||
setting_dns_overrides: Vec::new(),
|
||||
setting_send_cookies: true,
|
||||
setting_store_cookies: true,
|
||||
setting_http_version: HttpVersion::Auto,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -516,6 +566,7 @@ pub struct Workspace {
|
||||
pub setting_dns_overrides: Vec<DnsOverride>,
|
||||
pub setting_send_cookies: bool,
|
||||
pub setting_store_cookies: bool,
|
||||
pub setting_http_version: HttpVersion,
|
||||
}
|
||||
|
||||
impl UpsertModelInfo for Workspace {
|
||||
@@ -560,6 +611,7 @@ impl UpsertModelInfo for Workspace {
|
||||
(SettingDnsOverrides, serde_json::to_string(&self.setting_dns_overrides)?.into()),
|
||||
(SettingSendCookies, self.setting_send_cookies.into()),
|
||||
(SettingStoreCookies, self.setting_store_cookies.into()),
|
||||
(SettingHttpVersion, self.setting_http_version.to_string().into()),
|
||||
])
|
||||
}
|
||||
|
||||
@@ -579,6 +631,7 @@ impl UpsertModelInfo for Workspace {
|
||||
WorkspaceIden::SettingDnsOverrides,
|
||||
WorkspaceIden::SettingSendCookies,
|
||||
WorkspaceIden::SettingStoreCookies,
|
||||
WorkspaceIden::SettingHttpVersion,
|
||||
]
|
||||
}
|
||||
|
||||
@@ -589,6 +642,7 @@ impl UpsertModelInfo for Workspace {
|
||||
let headers: String = row.get("headers")?;
|
||||
let authentication: String = row.get("authentication")?;
|
||||
let setting_dns_overrides: String = row.get("setting_dns_overrides")?;
|
||||
let setting_http_version: String = row.get("setting_http_version")?;
|
||||
Ok(Self {
|
||||
id: row.get("id")?,
|
||||
model: row.get("model")?,
|
||||
@@ -607,6 +661,7 @@ impl UpsertModelInfo for Workspace {
|
||||
setting_dns_overrides: serde_json::from_str(&setting_dns_overrides).unwrap_or_default(),
|
||||
setting_send_cookies: row.get("setting_send_cookies")?,
|
||||
setting_store_cookies: row.get("setting_store_cookies")?,
|
||||
setting_http_version: setting_http_version.parse().unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1078,6 +1133,7 @@ impl Default for Folder {
|
||||
enabled: false,
|
||||
value: DEFAULT_REQUEST_MESSAGE_SIZE,
|
||||
},
|
||||
setting_http_version: InheritedHttpVersionSetting::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1108,6 +1164,7 @@ pub struct Folder {
|
||||
pub setting_follow_redirects: InheritedBoolSetting,
|
||||
pub setting_request_timeout: InheritedIntSetting,
|
||||
pub setting_request_message_size: InheritedIntSetting,
|
||||
pub setting_http_version: InheritedHttpVersionSetting,
|
||||
}
|
||||
|
||||
impl UpsertModelInfo for Folder {
|
||||
@@ -1159,6 +1216,7 @@ impl UpsertModelInfo for Folder {
|
||||
SettingRequestMessageSize,
|
||||
serde_json::to_string(&self.setting_request_message_size)?.into(),
|
||||
),
|
||||
(SettingHttpVersion, serde_json::to_string(&self.setting_http_version)?.into()),
|
||||
])
|
||||
}
|
||||
|
||||
@@ -1178,6 +1236,7 @@ impl UpsertModelInfo for Folder {
|
||||
FolderIden::SettingFollowRedirects,
|
||||
FolderIden::SettingRequestTimeout,
|
||||
FolderIden::SettingRequestMessageSize,
|
||||
FolderIden::SettingHttpVersion,
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1193,6 +1252,7 @@ impl UpsertModelInfo for Folder {
|
||||
let setting_follow_redirects: String = row.get("setting_follow_redirects")?;
|
||||
let setting_request_timeout: String = row.get("setting_request_timeout")?;
|
||||
let setting_request_message_size: String = row.get("setting_request_message_size")?;
|
||||
let setting_http_version: String = row.get("setting_http_version")?;
|
||||
Ok(Self {
|
||||
id: row.get("id")?,
|
||||
model: row.get("model")?,
|
||||
@@ -1216,6 +1276,7 @@ impl UpsertModelInfo for Folder {
|
||||
.unwrap_or_default(),
|
||||
setting_request_message_size: serde_json::from_str(&setting_request_message_size)
|
||||
.unwrap_or_else(|_| default_request_message_size_setting()),
|
||||
setting_http_version: serde_json::from_str(&setting_http_version).unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1283,6 +1344,7 @@ impl Default for HttpRequest {
|
||||
setting_validate_certificates: InheritedBoolSetting::default(),
|
||||
setting_follow_redirects: InheritedBoolSetting::default(),
|
||||
setting_request_timeout: InheritedIntSetting::default(),
|
||||
setting_http_version: InheritedHttpVersionSetting::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1319,6 +1381,7 @@ pub struct HttpRequest {
|
||||
pub setting_validate_certificates: InheritedBoolSetting,
|
||||
pub setting_follow_redirects: InheritedBoolSetting,
|
||||
pub setting_request_timeout: InheritedIntSetting,
|
||||
pub setting_http_version: InheritedHttpVersionSetting,
|
||||
}
|
||||
|
||||
impl UpsertModelInfo for HttpRequest {
|
||||
@@ -1370,6 +1433,7 @@ impl UpsertModelInfo for HttpRequest {
|
||||
),
|
||||
(SettingFollowRedirects, serde_json::to_string(&self.setting_follow_redirects)?.into()),
|
||||
(SettingRequestTimeout, serde_json::to_string(&self.setting_request_timeout)?.into()),
|
||||
(SettingHttpVersion, serde_json::to_string(&self.setting_http_version)?.into()),
|
||||
])
|
||||
}
|
||||
|
||||
@@ -1394,6 +1458,7 @@ impl UpsertModelInfo for HttpRequest {
|
||||
SettingValidateCertificates,
|
||||
SettingFollowRedirects,
|
||||
SettingRequestTimeout,
|
||||
SettingHttpVersion,
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1407,6 +1472,7 @@ impl UpsertModelInfo for HttpRequest {
|
||||
let setting_validate_certificates: String = row.get("setting_validate_certificates")?;
|
||||
let setting_follow_redirects: String = row.get("setting_follow_redirects")?;
|
||||
let setting_request_timeout: String = row.get("setting_request_timeout")?;
|
||||
let setting_http_version: String = row.get("setting_http_version")?;
|
||||
Ok(Self {
|
||||
id: row.get("id")?,
|
||||
model: row.get("model")?,
|
||||
@@ -1433,6 +1499,7 @@ impl UpsertModelInfo for HttpRequest {
|
||||
.unwrap_or_default(),
|
||||
setting_request_timeout: serde_json::from_str(&setting_request_timeout)
|
||||
.unwrap_or_default(),
|
||||
setting_http_version: serde_json::from_str(&setting_http_version).unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,6 +208,14 @@ impl<'a> ClientDb<'a> {
|
||||
} else {
|
||||
parent.store_cookies
|
||||
},
|
||||
http_version: if folder.setting_http_version.enabled {
|
||||
ResolvedSetting::from_model(
|
||||
folder.setting_http_version.value,
|
||||
AnyModel::Folder(folder.clone()),
|
||||
)
|
||||
} else {
|
||||
parent.http_version
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,6 +153,14 @@ impl<'a> ClientDb<'a> {
|
||||
} else {
|
||||
parent.store_cookies
|
||||
},
|
||||
http_version: if http_request.setting_http_version.enabled {
|
||||
ResolvedSetting::from_model(
|
||||
http_request.setting_http_version.value,
|
||||
AnyModel::HttpRequest(http_request.clone()),
|
||||
)
|
||||
} else {
|
||||
parent.http_version
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -174,7 +182,10 @@ impl<'a> ClientDb<'a> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::init_in_memory;
|
||||
use crate::models::{HttpRequest, HttpRequestHeader};
|
||||
use crate::models::{
|
||||
Folder, HttpRequest, HttpRequestHeader, HttpVersion, InheritedHttpVersionSetting, Workspace,
|
||||
};
|
||||
use crate::util::UpdateSource;
|
||||
|
||||
#[test]
|
||||
fn request_resolution_preserves_duplicate_request_headers() {
|
||||
@@ -210,4 +221,77 @@ mod tests {
|
||||
assert_eq!(cookies[1].value, "optional=1");
|
||||
assert!(!cookies[1].enabled);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn http_version_resolves_through_the_inheritance_chain() {
|
||||
let (query_manager, _blob_manager, _rx) = init_in_memory().expect("Failed to init DB");
|
||||
let db = query_manager.connect();
|
||||
|
||||
let workspace = db
|
||||
.upsert_workspace(
|
||||
&Workspace {
|
||||
name: "Test".to_string(),
|
||||
setting_http_version: HttpVersion::Http2,
|
||||
..Default::default()
|
||||
},
|
||||
&UpdateSource::Background,
|
||||
)
|
||||
.expect("Failed to upsert workspace");
|
||||
|
||||
let folder = db
|
||||
.upsert_folder(
|
||||
&Folder { workspace_id: workspace.id.clone(), ..Default::default() },
|
||||
&UpdateSource::Background,
|
||||
)
|
||||
.expect("Failed to upsert folder");
|
||||
|
||||
let request = db
|
||||
.upsert_http_request(
|
||||
&HttpRequest {
|
||||
workspace_id: workspace.id.clone(),
|
||||
folder_id: Some(folder.id.clone()),
|
||||
..Default::default()
|
||||
},
|
||||
&UpdateSource::Background,
|
||||
)
|
||||
.expect("Failed to upsert request");
|
||||
|
||||
// No overrides, so the workspace base value applies
|
||||
let resolved = db.resolve_settings_for_http_request(&request).expect("Failed to resolve");
|
||||
assert_eq!(resolved.http_version.value, HttpVersion::Http2);
|
||||
assert_eq!(resolved.http_version.source_model, "workspace");
|
||||
|
||||
// A folder override beats the workspace base
|
||||
db.upsert_folder(
|
||||
&Folder {
|
||||
setting_http_version: InheritedHttpVersionSetting {
|
||||
enabled: true,
|
||||
value: HttpVersion::Http1,
|
||||
},
|
||||
..folder
|
||||
},
|
||||
&UpdateSource::Background,
|
||||
)
|
||||
.expect("Failed to update folder");
|
||||
let resolved = db.resolve_settings_for_http_request(&request).expect("Failed to resolve");
|
||||
assert_eq!(resolved.http_version.value, HttpVersion::Http1);
|
||||
assert_eq!(resolved.http_version.source_model, "folder");
|
||||
|
||||
// A request override beats them both
|
||||
let request = db
|
||||
.upsert_http_request(
|
||||
&HttpRequest {
|
||||
setting_http_version: InheritedHttpVersionSetting {
|
||||
enabled: true,
|
||||
value: HttpVersion::Auto,
|
||||
},
|
||||
..request
|
||||
},
|
||||
&UpdateSource::Background,
|
||||
)
|
||||
.expect("Failed to update request");
|
||||
let resolved = db.resolve_settings_for_http_request(&request).expect("Failed to resolve");
|
||||
assert_eq!(resolved.http_version.value, HttpVersion::Auto);
|
||||
assert_eq!(resolved.http_version.source_model, "http_request");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,8 +96,8 @@ impl<'a> ClientDb<'a> {
|
||||
deleted
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = conn
|
||||
.execute_batch("ROLLBACK TO delete_workspace; RELEASE delete_workspace");
|
||||
let _ =
|
||||
conn.execute_batch("ROLLBACK TO delete_workspace; RELEASE delete_workspace");
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
@@ -177,6 +177,10 @@ impl<'a> ClientDb<'a> {
|
||||
workspace.setting_store_cookies,
|
||||
AnyModel::Workspace(workspace.clone()),
|
||||
),
|
||||
http_version: ResolvedSetting::from_model(
|
||||
workspace.setting_http_version,
|
||||
AnyModel::Workspace(workspace.clone()),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user